作者归档:starlight36

Java微服务的模块划分

近段时间在推进公司内Java微服务的框架升级,有机会直面一线业务的很多服务,在这个过程中也发现一些有趣的事情:不同时间段,不同团队,大家对于Java服务的模块划分、模块或者Jar包发布、模块包的依赖管理做法都有所不同,今天正好借这篇文章,和大家聊一下。

从再熟悉不过的模块结构划分开始

在盘点业务服务时发现,大多数项目都采取了如下或者与之类似的项目模块结构:

  • business-api —— 对外发布的API接口,例如Dubbo协议的Service
  • business-dao —— 数据访问对象,多数项目使用了MyBatis,这里是Mapper的聚集地
  • business-model —— 模型,包括各类的实体、DTO等
  • business-service —— 业务逻辑层,包括各类Service、接口、实现等
  • business-web —— Web请求处理层,包括Controller等

它的命名规则可以概括为<服务名>-<模块名>,除了上面列出的模块外,常见的可能还有像business-core、business-common等。这种模块划分模式很常见,在大量的Java服务项目中得以采用。模块划分的依据也就是应用的分层结构,例如阿里巴巴的Java开发手册中推荐的应用分层模式为:

来自阿里巴巴Java开发手册,建议的应用分层

我们可以把这种类型的项目模块划分看做对这种分层模式的一种落地,所以要探讨模块划分的问题,我们需要先了解一下应用分层模式的由来。

继续阅读

基于Twitter的Snowflake算法实现发号器

在微服务架构的系统中,ID号的生成是一个需要考虑的问题。通常单体系统会依赖RDB的自增字段(例如MySQL)或者序列(例如PostgreSQL等)来产生业务序号。在微服务架构的系统中也使用类似的方式时就会出现一些问题。

在单体系统中,我们可能会使用自增字段,或者序列,它们通常依赖于关系型数据库插入动作时产生的ID。不过由于ID是在持久化到数据库时才产生,所以我们无法提前获取一个ID号。对于单体系统而言并没有什么问题,但当存在多个系统协作时,我们可能希望提前生成业务的ID号,以保证业务可以以幂等的方式进行操作。为了解决这个问题,ID生成可以先独立出单独的模块,每次产生一个新的ID号供业务方使用。

不过利用RDB的序列(MySQL可以模拟一个序列)来进行发号,通常存在性能瓶颈。因为业务操作必须先在RDB操作后,才能取得新的ID号。一旦RDB是单点系统,那么所有业务的可用性都会受到影响。另外连续的ID号可能会暴露业务的信息,通过ID号可以间接地刺探到业务的规模,也更容易受到攻击,一旦存在权限验证不严的系统,攻击者可以轻而易举的枚举全部的信息访问。

UUID是一种专门设计用来在分布式系统中生成ID的工具。它可以产生一个128bit的ID号。目前常见的版本是v1和v4,其中v1版本使用时间戳、MAC地址、随机数来生成一个ID号,v4版本使用随机数来产生ID号。但是UUID通常情况下过于冗长,存储为一个32+4个字符串,作为业务ID使用并不方便,另外UUID依然存在冲突的可能性,只不过这种可能系非常低。

使用UUID用做分布式系统的ID生成器是一种选择,但是更多时候我们希望ID号可以:

  • 生成高效,可分布式并行发号
  • 大致时间顺序(对于数据分片、排序友好)
  • 稀疏不连续
  • 可以使用64bit整型存储
  • 可预见的时间范围内不重复

Twitter开源了他们的ID生成器,被称作snowflake,项目地址在https://github.com/twitter/snowflake。不过目前Twitter已经完全重写了他们的发号器,因此这个项目现在处于关闭状态,参考源代码只能查看2010年的初始tag,目前也不再维护这个项目了。

继续阅读

Restful中PUT和POST的区别

在编写Restful风格服务时经常搞混PUT和POST方法,只是隐隐约约记得一个用于创建一个用于更新,但是对这个并不是特别确定,今天正好确认一番。爆栈网有一个回答很清晰:

对于PUT和POST而言,他们都可以用来创建资源。

那么到底哪一个应当使用呢?通常考虑以下点:

  • 如果你在请求时指定资源ID,那么用PUT;你期望服务器自动生成资源ID,那么用POST;
  • PUT是幂等的,重复调用两次不会造成额外的影响,所以PUT可以被安全的重试;
  • PUT可以用来更新或者创建资源;
  • POST方法在同时访问时,可能会修改一个资源对象的不同部分。

原问题:http://stackoverflow.com/questions/630453/put-vs-post-in-rest

[翻译]为Kubernetes创建自定义Pod水平扩展器

原文链接:Building your own horizontal pod autoscaler for Kubernetes

当前版本(1.3)的Kubernetes为在生产环境中平滑运行容器化应用提供了大量开箱即用的特性。不过一些特性依然不够完善,比如Pod水平扩展器(Horizontal pod autoscaler, HPA)。现在你只能根据CPU和内存使用水平进行扩容调度,自定义指标的调度目前仅在Alpha版本中支持。

我们其中一个应用是一个Websocket服务器,用来处理来自客户端的长连接。然而性能测试显示我们的应用最大可以承载大约25000个Websocket活跃连接,更多的连接数会导致服务器不稳定甚至崩溃,然而这时通常不会引起CPU负载的升高和内存开销的增长。所以让Kubernetes根据Websocket连接数进行扩展调度的需求应运而生。本文介绍了我们创建自定义水平扩展器(HPA)的一些实践。

继续阅读

HTTP/2升级小记

最近H5被插广告愈演愈烈,于是打算给H5增加HTTPS支持。既然都支持了HTTPS,那么HTTP/2也就顺便来支持一下好了,反正SSL证书已经不是问题了。

我们是全容器环境,包括前端机在内,全部跑在容器里,更新前端nginx容器配置即可。Nginx从1.9.5版本开始支持HTTP/2的,查了一下满足条件。从HTTPS到HTTP/2,只需要:

server {
    listen 443 ssl http2;
}

即可。

但是会有一些问题。协议协商包括NPN和ALPN两种,NPN是SPDY时代的产物,HTTP/2定稿时已经被ALPN取代。我们的目标是支持HTTP/2,因此务必支持ALPN。 继续阅读

[翻译]如何在Docker中使用Open vSwitch创建跨主机的容器网络

翻译自原文:http://openvswitch.org/support/dist-docs/INSTALL.Docker.md.txt

本文介绍了如何使用Open vSwitch为Docker 1.9.0及以后版本提供网络支持。操作前请先确认你已经按照[INSTALL.md] 编译,或者通过包管理器安装了Open vSwitch。关于如何安装Docker,请参考www.docker.com上提供的介绍。

Docker从1.9.0版本之后提供了跨主机的网络支持。通过将Docker和Open vSwitch整合,则可以利用Open vSwitch virtual network (OVN)进行互联互通。
继续阅读

分享一个VPN自动检测和断线重连

由于很多你懂得的原因,我们不得不通过一些技术手段来访问技术网站。由于线路不稳定或者很多其他的因素,会引起PPTP的假死和中断。所以我在服务器上写了一个PPTP Client的自动检测拉起脚本,来确保线路畅通。

不多说了,上代码。

#!/bin/bash

SCRIPT_PIDS=$(pidof -x $0)
if [ `echo $SCRIPT_PIDS | awk -F' ' '{print NF; exit}'` -gt 1 ]; then
    echo "VPN Guard is already running..."
    exit
fi

function select_fast_server() {
    LIST=""
    for ip in `cat /etc/ppp/peers/vpn-server`
    do
        LIST=$LIST"$ip `ping -c 3 -t 3 $ip | grep 'time=' | awk '{print $7}' | cut -f 2 -d '=' | awk '{sum += $1};END { if (NR > 0) print sum / NR }'`\n"
    done

    FAST_HOST=$(echo -e $LIST | awk 'NR == 1 {ip = $1; min = 99999} $2 > 0 && $2 < min {ip = $1; min = $2} END {print ip}')

    sed -i "s|pptp.*--nolaunchpppd|pptp ${FAST_HOST} --nolaunchpppd|g" /etc/ppp/peers/vpn

    echo "Fast vpn server is ${FAST_HOST}"
}

while [ true ]; do
    if [ `ps -ef | grep "pptp" | grep -v grep | wc -l` -lt 1 ]; then
        select_fast_server
        sleep 1
        /usr/sbin/pppd call vpn
        sleep 15
    else
        if [ `httping -G -c 3 -i 3 -t 2 http://www.google.com 2>&1 | grep "connected to" | wc -l` -lt 1 ]; then
            kill -9 `ps -ef | grep -E '(pptp|pppd)' | grep -v grep | awk '{print $2}'`
        fi
    fi
    sleep 3
done

继续阅读