美文网首页Dapeng-soaJava专题
一个服务的前世今生-dapeng-soa服务生命周期详述

一个服务的前世今生-dapeng-soa服务生命周期详述

作者: Ever_00 | 来源:发表于2018-12-05 21:58 被阅读0次
    dapeng-soa architechure

    1. 概述

    随着搭载于dapeng框架之上的业务系统一个个上线并趋于稳定,不少开发同事们在抓虫子之余,也萌生了一些“不满”:用了dapeng好久了,但是它就像一个黑盒子,我们知之甚少,能否系统的介绍一下啊。
    确实,dapeng框架提供了丰富的脚手架以及详尽的开发指南(详见dapeng官网), 极易上手。但了解一下dapeng的内部机制,有利于开阔眼界,并在开发中充分利用各个特性,对于提高服务的质量也很有好处。

    本文着眼于分析一个服务从启动到结束的整个过程。

    后续会分专题陆续介绍dapeng框架的各个特性,敬请留意。

    2. dapeng 容器目录结构

    dapeng-container
    ├── apps                           # 1. business services
    │   ├── order_service
    │   └── stock_service
    ├── bin
    │   ├── dapeng-bootstrap.jar                      # 2. dapeng bootstrap module
    │   ├── lib                                       # 3. dapeng container jars(including dependency)
    │   ├── shutdown.sh                               # 4. startup/shutdown shell
    │   └── startup.sh
    ├── conf                                          # 5. configuration files for container
    │   └── logback.xml
    ├── lib                                           # 6. core lib
    │   ├── dapeng-core-2.1.1.jar
    │   └── logback-*.jar
    ├── logs                                          # 7. directory where log files located
    └── plugin                                        # 8. plugin directory
    
    1. 业务服务,该目录由dapeng的ApplicationClassLoader加载, 可在一个dapeng容器中放多个服务,但是建议一个容器一个服务
    2. dapeng bootstrap模块,容器的启动入口,负责生成其它的定制classLoader并启动容器。
    3. dapeng container jar目录,容器的实现类及其三方依赖,通过ContainerClassLoader加载
    4. 启动以及停止脚本
    5. 容器的配置文件
    6. 核心接口库以及日志,由CoreClassLoader负责加载,可用于服务端以及客户端
    7. 日志目录
    8. 插件目录,由PluginClassLoader加载

    注意, 不同的目录由不同的classLoader加载。常见的错误是:apps路径下的服务有个对象(通过ApplicationClassLoader加载),假设叫a,其类型为A,它是单例对象(通过A.getInstance()获得)。但是在bin路径下(也就是通过ContainerClassLoader加载), 通过A.getInstance()得到的a', 就跟a是完全不同的对象了。

    3. zk节点结构

    /soa/
    ├── runtime/service/                                   # 1. runtime info
    │   ├── com.today.soa.idgen.service.IDService/
    │   │   ├── 192.168.10.130:9081:1.0.0:0000000181        # 2. node info
    │   │   ├── 192.168.20.101:9081:1.0.0:0000000179        # 3. master node
    │   │   └── 192.168.10.138:9081:1.0.0:0000000183
    │   ├── com.today.api.order.service.OrderService2/ 
    │   │   ├── 192.168.10.133:9099:1.0.0:0000000368        
    │   │   ├── 192.168.10.126:9099:1.0.0:0000000372       
    │   │   ├── 192.168.20.102:9099:1.0.0:0000000367      
    │   │   └── 192.168.10.131:9099:1.0.0:0000000366        # master node
    │   └── ...                                             # 4. other services
    └── config/
        ├── services/                                       # 5. config info
        │   ├── com.today.soa.idgen.service.IDService
        │   └── com.today.api.order.service.OrderService2
        ├── freq/                                           # 6. rate limit rules
        ├── routes/                                         # 7. router rules
        └── cookies/                                        # 8. cookie rules
    

    3.1 服务运行时信息 /soa/runtime/service/

    服务运行时信息由服务在启动的时候注册到zk的/soa/runtime/service/${serviceName}路径上,其表现形式为挂靠在该路径下的临时节点,格式为:ip:port:version:seq, seq由zookeeper自动生成,且能保证在同目录下唯一并单调递增。
    其中,dapeng容器根据本容器中服务运行时信息的seq,判断是否为master主节点(seq最低者为master)

    一个服务对应一个临时节点。 我们可通过该路径得知目前服务集群中该服务的节点数量

    3.2 服务配置信息 /soa/config/

    服务配置信息存放了dapeng-soa所需要的一些配置项,可通过命令行工具或者配置中心进行管理。

    服务的配置信息当前有四种,分布在/soa/config/不同的子目录下。它们的结构都类似,都是把服务名字作为节点挂在配置子目录下,然后配置信息作为节点的内容。

    3.2.1 普通配置信息 /soa/config/services/

    包括负载均衡策略、服务超时等信息。
    包含两个层次:

    • 全局性配置: 直接写进/soa/config/services/节点
    timeout/100ms;loadbalance/LeastActive;
    
    • 服务私有配置: 写到具体的服务节点上, 例如/soa/config/services/com.today.api.order.service.OrderService2
    timeout/100ms,createOrder:500ms,createOrderPayment:60000ms;loadbalance/LeastActive,createOrder:Random,createOrderPayment:RoundRobin;
    

    3.2.2 限流配置信息 /soa/config/freq/

    dapeng 的流控做在服务端,所以该节点只对服务端有效
    限流信息直接写在具体的服务节点上,例如如下订单的限流配置写在/soa/config/freq/com.today.api.order.service.OrderService2

    [rule1]
    match_app = listOrder # 针对具体方法限流
    rule_type = callerIp # 对每个请求端IP
    min_interval = 60,5  # 每分钟请求数不超过5次
    mid_interval = 3600,100 # 每小时请求数不超过100次
    max_interval = 86400,200 # 每天请求数不超过200次
    
    [rule2]
    match_app = * # 针对订单服务限流
    rule_type = callerIp # 对每个请求端IP
    min_interval = 60,600  # 每分钟请求数不超过600
    mid_interval = 3600,10000 # 每小时请求数不超过1万
    max_interval = 86400,80000 # 每天请求数不超过8万
    

    详见dapeng-soa限流文档

    3.2.3 路由配置信息 /soa/config/routes/

    服务路由信息也是直接写在具体的服务节点上,例如下面订单的路由配置写在/soa/config/routes/com.today.api.order.service.OrderService2

    method match r"create.*" => ip"192.168.10.0/24"
    cookie_storeId match %"10n+1..6" => ip"192.168.20.128"
    

    详见dapeng-soa路由文档

    3.2.3 cookie 规则信息 /soa/config/cookies/

    cookie规则信息也是直接写在具体的服务节点上,例如针对来自dapengCli的手工对订单接口的调用,我们为这些调用打开TRACE功能,那么我们把规则配置到/soa/config/cookies/com.today.api.order.service.OrderService2:

    callerIp match ip"192.168.20.200" => c"thread-log-level#TRACE"
    

    3. 容器的生命周期

    容器提供了一个LifeCycleAware接口以及若干事件,在事件发生的时候会触发相应的业务逻辑。
    业务可通过实现该接口, 做一些初始化以及清理的动作。

    例如某服务的业务,只在主节点启动一个工作线程。那么它就可以监听MASTER_CHANGE事件。当主节点发生变更的时候,就启动或者停止工作线程。

        public enum LifeCycleEventEnum {
            /**
             * dapeng 容器启动
             */
            START,
            PAUSE,
            MASTER_CHANGE,
            CONFIG_CHANGE,
            STOP
        }
    
    /**
     * 提供给业务的lifecycle接口,四种状态
     *
     * @author hui
     * @date 2018/7/26 11:21
     */
    public interface LifeCycleAware {
    
        /**
         * 容器启动时回调方法
         */
        void onStart(LifeCycleEvent event);
    
        /**
         * 容器暂停时回调方法
         */
        default void onPause(LifeCycleEvent event) {
        }
    
        /**
         * 容器内某服务master状态改变时回调方法
         * 业务实现方可自行判断具体的服务是否是master, 从而执行相应的逻辑
         */
        default void onMasterChange(LifeCycleEvent event) {
        }
    
        /**
         * 容器关闭
         */
        void onStop(LifeCycleEvent event);
    
        /**
         * 配置变化
         */
        default void onConfigChange(LifeCycleEvent event) {
        }
    }
    

    4. 容器的启动过程

    容器启动需要协调各个插件的顺序,避免在服务还没准备好的情况下,客户端请求就涌进来。
    通过脚本startup.sh启动容器: java -server $JAVA_OPTS -cp ./dapeng-bootstrap.jar com.github.dapeng.bootstrap.Bootstrap

    dapeng-soa bootstrap
    1. Bootstrap创建三个classLoader, 分别是CoreClassLoader(负责加载lib目录的类)、ContainerClassLoader(负责加载bin/lib目录的类)以及ApplicationClassLoader(负责加载apps目录下的类)。

    2. Bootstrap通过ContainerClassLoader加载ContainerFactory并调用其getContainer方法, 获得DapengContainer实例。

    3. DapengContainer创建五个Plugin,并依次调用其start方法:
      3.1 启动NettyPlugin, 打开服务监听端口(例如9090)
      3.2 启动ZkPlugin, 跟注册中心zookeeper建立连接。
      3.3 启动SpringPlugin
      3.3.1 通过ApplicationClassLoader加载服务(一个服务表现为一个Application对象)
      3.3.2 对每个服务(假设为OrderService),通过ZkPlugin把服务信息注册到/soa/runtime/service/com.today.api.order.service.OrderService2目录里,并启动一个对该路径的zk watcher(主要用于跟踪服务集群中master节点的变化, 当发生master变换时,需触发MASTER_CHANGE事件。

      一旦服务完成注册,嗷嗷待哺的客户端就会如潮水般涌进该服务节点。

      3.4 启动SchedulePlugin, 定时任务就绪
      3.5 启动JmxPlugin, Jmx 端口就绪
      3.6 如果是开发模式(默认), 那么启动ApiDocPlugin, 内置的文档站点可访问。该插件在生产环境下不会启动

    4. 触发容器START事件

    5. 加载服务端Filter(详情下回分解)。

    6. 最后,注册Jvm进程的ShutdownHook

    至此,容器启动完毕, 服务生命周期开始。

    5. 容器的优雅关闭过程

    容器的关闭过程,同样需要协调插件的关闭顺序,确保进来的请求尽量处理完毕后再关闭容器,避免对业务产生影响。为此, dapeng 容器会维护一个请求计数器requestCounter,计数值是当前容器内尚未处理完的请求数目。

    1. 启动脚本startup.sh会监听kill信号。收到kill信号后,脚本转发该信号到容器Jvm进程。
    2. 容器的ShutdownHook给触发,依次执行:
      2.1 ZkPlugin.stop方法,断开跟zookeeper的连接,从而把本节点服务信息从/soa/runtime/services/${serviceName}上摘除,进而新的请求就不会再路由到本节点。
      2.2 休眠若干次,直到requestCounter数目为0或者超时。
      2.3 关闭其它插件( Netty有自己的优雅关闭机制,能确保outbound队列的消息能全部发送出去)

    至此,容器关闭,服务结束了其使命。

    image

    相关文章

      网友评论

        本文标题:一个服务的前世今生-dapeng-soa服务生命周期详述

        本文链接:https://www.haomeiwen.com/subject/hnihcqtx.html