导语:宜信于 2019 年 3 月 29 日正式开源 nextsystem4(以下简称“NS4”)系列模块。此次开源的 NS4 系列模块是围绕当前支付系统笨重、代码耦合度高、维护成本高而产生的分布式业务系统解决方案。NS4 系列框架允许创建复杂的流程 / 业务流,对于业务服务节点的实现可串联,可分布式。其精简、轻量,实现了“脱容器”(不依赖 tomcat、jetty 等容器)独立运行。NS4 系列框架的设计理念是将业务和逻辑进行分离,开发人员只需通过简单的配置和业务实现就可以实现逻辑复杂、性能高效、功能稳定的业务系统。【点击查看框架整体介绍】
NS4 系列包括 4 个开源模块,分别是:ns4_frame 分布式服务框架、ns4_gear_idgen ID 生成器组件(NS4 框架 Demo 示例)、ns4_gear_watchdog 监控系统组件(服务守护、应用性能监控、数据采集、自动化报警系统)和 ns4_chatbot 通讯组件。本文将详细介绍 ns4_frame 分布式服务框架开发指南。
项目地址:https://github.com/newsettle/…
一、框架介绍
ns4_frame 本质上是两个应用加三套开发框架组合起来的分布式业务框架。
1.1 使用范围
ns4_frame 分布式框架主要适用于业务类型为消息流或者业务核心模型为流式业务的业务系统。它支持消息分发、传递、追踪,支持分步骤、分批次的消息处理,对于信息流、数据流等消息驱动型的业务尤为契合。
1.2 项目结构
ns4_frame 框架是一套 MAVEN 父子项目,由五个项目组成:
NS_MQ:负责和底层消息队列进行通信,提供了对消息队列进行操作的 API。
NS_TRANSPORTER:通过调用 NS_MQ 提供的 API, 对业务消息进行收取、处理、转发。
NS_CHAIN:一个可选开发框架,负责对同一个 jvm 中的业务处理步骤进行链条式的整合,组成当前业务模块的业务处理流程。
NS_CONTROLLER:一个业务消息转发应用,负责将接收到的消息转给对应的业务模块进行处理,同时负责将业务模块根据整体业务进行关联。NS_CONTROLLER 本质是一个独立的应用系统,构建于 NS_TRANPORTOR 和 NS_CHAIN 之上。
NS_DISPATCHER:ns4_frame 架构规定的消息入口,通过提供的 http 服务接受业务系统边界外的 http 请求,并将请求转化成业务系统内部通信使用的消息协议格式。
二、基础入⻔
2.1 开发环境配置
开发语言:JAVA
JDK 版本:JDK1.7
MAVEN 版本:3.3 以上
REDIS 版本:3.0 以上
以上是开发环境必备的组件和配置,其中 java 为开发语言,maven 为项目编译打包部署必备, redis 作为消息中间件使用。
2.2 运行
ns4_frame 运行至少需要启动三个 jvm 项目才能完整运行。启动整个项目分为如下三步:
第一步: ns4_frame 消息入口是一个 http 接口,http 服务是由 NS_DISPATCHER 项目提供的,所以我们第一件事情就是要把 NS_DISPATCHER 运行起来。要运行 NS_DISPATCHER,直接运行 Bootstrap 类的 main 方法即可。默认的 NS_DISPATCHER 会在本机 (127.0.0.1) 的 8027 端口上监听 http 请求, 如果收到 http 请求,默认会将 http 请求转换成内部通信消息,并存储到本机 (127.0.0.1) 的 redis 中,默认访问的 redis 端口号是 6379。
第二步: NS_CONTROLLER 负责接收 NS_DISPATCHER 传入的消息,并根据配置进行消息分发。所以我们随后需要运行 NS_CONTROLLER 项目(为了方便,以下简称“CONTROLLER”)。在 CONTROLLER 项目中我们不能直接运行,需要配置一些东⻄。CONTROLLER 要运行至少需要指定一个配置文件位置。这个配置文件需要通过 java 命令参数来指定。假设我现在指定 java 运行参数 -Dconfigfile=nscontroller.xml 这个参数本质上是给 CONTROLLER 底层的 NS_TRANSPORTER 使用的,它指明了 NS_TRANSPORTER 必须得配置文件位置,使得 CONTROLLER 能顺利利用 NS_TRANSPORTER 进行消息收发。默认情况下,CONTROLLER 还会到 classpath 下去找关于 NS_CHAIN 需要的配置文件,默认路径是 classpath 下的 nschainconfig 目录,在这个目录下所有的 xml 文件会被认作是 NS_CHAIN 需要的配置文件集合。当配置文件配置好后,可以通过调用 com.creditease.ns.transporter.context.XmlAppTransporterContext 的 main 方法来启动 NS_CONTROLLER。
第三步: 在这个步骤中我们需要启动自己的业务项目,在这个业务项目中,必须有以下三个前置条件:
业务项目需要建立在 NS_TRANSPORTER 框架之上。
业务项目的消息队列名称必须和 CONTROLLER 项目中配置的队列名一致。
业务启动必须通过 com.creditease.ns.transporter.context.XmlAppTransporterContext 的 main 方法来启动。
完成以上的三个步骤,一个基本的 ns4_frame 系统就搭建好并运行起来了。
三、项目架构
3.1 层次划分
上图展示了 ns4_frame 每个系统的层次结构。
底层是以 redis 作为消息中间件,对消息中间件的操作被封装入了 NS_MQ 项目,它向上层提供了对消息队列的操作 API 接口。
在 NS_MQ 的上层是 NS_TRANSPORTER, 它本质是一套消息收发处理框架,它负责接收消息后反向回调业务代码,并将消息交给业务层处理。当业务层处理完毕后,它负责将处理后的消息返回到 redis 中。
NS_CHAINS 是一套开发辅助框架,它负责将一个模块的业务处理步骤解耦成一个个零散的任务,并可以随意以任何顺序做关联。
NS_CONTROLLER 是一个项目,它本质上是一个独立的应用,它负责将整体业务分解成一个个节点,并通过配置将他们以一定的顺序关联起来,并通过消息机制,将这些节点结合起来 形成一套业务系统。
NS_DISPATCHER 也是一个项目,它是以 NETTY 框架作为基础,开发出的一个能提供基本的 http 服务的独立应用。同时它也是业务系统和外部通讯的唯一边界。
3.2 运行流程
ns4_frame 整套系统本质上其实就是一套消息中间件服务加开发框架,整体的结构图如下:
上图显示了一个 ns4_frame 整体分布式项目的运行流程,一个消息的运转流程按如下顺序:
NS_DISPATCHER 收到 http 请求,并将 http 请求转化为内部消息协议放入指定的消息队列中(根据配置文件)。
NS_CONTORLLER 从步骤 1 指定的队列接收到消息,并根据配置的服务编排开始按照顺序将消息发送到每个服务步骤对应的消息队列中。
业务系统收到步骤 2 中 NS_CONTROLLER 指定的消息队列接收到消息并开始处理,处理完毕后,将结果返回。
NS_CONTROLLER 收到业务系统的响应,开始根据配置好的服务,将返回的消息结果发送到下一个服务对应的消息队列中。
四、NS_MQ 框架介绍
4.1 核心类和接口
RedisMQTemplate 类:封装了所有和消息队列的操作相关的 API
MQConfig:存储了所有和底层消息中间件相关的配置。
4.2 配置方案
默认的,在没有做任何配置的情况下,NS_MQ 会自动访问本机 (127.0.0.1) 的 6379 端口的 redis,如果没有,则会报异常。通常,NS_MQ 会去找 classpath 下一个名为 ns_mq.properties 的配置文件,这个配置文件中存储着所有和底层消息中间件相关的属性。
列举一些关键的配置元素:
redis.type 1 代表 redis 单机 2 代表 redis 集群 默认为 1
redis.single/cluster.host redis 单机或者集群的主机地址(包含端口)
redis.single/cluster.maxTotal redis 单机或者集群的最大连接数
redis.single/cluster.miniIdle redis 单机或者集群的最小闲置连接数
redis.single/cluster.maxIdle redis 单机或者集群的最大闲置连接数
redis.single/cluster.connectionTimeout redis 单机或者集群的尝试连接的超时时间(尚未连接到服务需要等待的时间)
redis.single/cluster.socketTimeout redis 单机或者集群连接后 socket 闲置的超时时间
五、NS_TRANSPORTER 框架介绍
5.1 框架架构
上图展示了整个 NS_TRANSPORTER 的整体架构,整套框架收发处理消息分为如下三个步骤:
首先由接收消息的线程 (Fetcher 线程) 通过 NS_MQ 从底层消息中间件获取消息并放入到本地消息缓存。
消息处理线程 (Handler 线程) 从本地消息缓存中取出消息,并调用业务层的方法实现对消息进行处理,处理完毕后,将处理后的消息放到本地发送缓存。
发送线程 (Sender 线程) 从本地发送缓存取出消息后,将消息通过 NS_MQ 将消息放入底层消息中间件。
5.2 核心类和接口
ServiceMessage:对各个模块之间传递的消息的 java 封装,包含了模块间通信需要知道的任何信息;
Worker:业务层需要实现此接口的 doWork 方法, 实现此接口的对象会被 NS_TRANSPORTER 的 Handler 线程回调用来对 ServiceMessage 中的信息进行处理。
ActionWorker:已经部分封装好的抽象类,实现了 Worker 接口,业务层可以直接继承这个抽象类,简化开发。
5.3 配置方案
默认的,NS_TRANSPORTER 会去找名为 configfile 的系统变量,这个系统变量的值就是 NS_TRANSPOTER 需要的配置文件所在的路径,NS_TRANSPORTER 会找到这个 xml 配置文件,并在解析相关的配置后启动。
NS_TRANSPORTER 相关的配置文件模板如下:
<queues>
<prefix></prefix>
<launchers>
<launcher>
<class name=” 类的全名 ” method=”method 方法名 ” property=”” />
<class name=” 类的全名 ” static-method=”method 方法名 ” /> </launcher>
</launchers>
<inqueues>
<queue>
<name></name>
<fetchernum></fetchernum> <buffersize></buffersize> <handlersize></handlersize> <serviceClass></serviceClass> <sendernum></sendernum>
</queue>
</inqueues>
</queues>
以上 xml 模板中有如下几个关键元素需要注意:
Launcher:用来定义在整个框架完全运行之前需要执行的方法。
queue:在这个元素下 name 元素表示需要监听的底层消息中间件的队列名。
Fetchernum:表示监听消息队列并获取消息的线程数,默认是 1。
buffersize:表示本地接收 / 发送消息队列的大小默认是 100。
handlersize:表示处理消息的线程数,默认是 10。
Serviceclass:表示具体的处理消息的业务类,这个类必须实现了 Worker 接口。
Sendernum:表示从本地发送消息队列中获取消息后发送到底层消息中间件的线程数。
六、NS_CHAIN 框架介绍
6.1 框架架构
由于 NS_CHAIN 本质是一个纯开发框架,故暂时忽略此框架的框架架构。
6.2 核心类和接口
暂略
6.3 配置方案
本节将详细介绍 NS_CHAINS 的配置。
NS_CHAINS 启动时会去找系统变量 chainconfig,这个变量的值就是 NS_CHAINS 配置文件所在的路径。NS_CHAINS 支持配置目录 (目录下的所有 xml 格式文件都被视作 NS_CHAINS 框架的配置文件) 和配置文件。
对于 NS_CHAINS 的配置格式我们大致列举出关键要素如下:
catalog:这个相当于一个完整的服务或者一个命名空间,是 NS_CHAINS 对外服务的基本单位,NS_CHAINS 外部系统只能看到 catalog。
Command:这是 NS_CHAINS 任务执行的最小单位,所有执行任务都可以以 command 的形式被调用执行。
Chain:这是一个 command 的容器,可以将多个 command 的任务组合成一个执行链路。
Group:这个一个 command 的组合,它可以将多个 command 组合成一个整体,并按照配置顺序执行。
同时 NS_CHAINS 的配置具有完整的逻辑语法,支持 if 条件判断,while 循环结构和顺序结构。
七、NS_DISPATCHER 应用介绍
7.1 框架架构
NS_DISPATCHER 本质是一个独立的建立在 Netty 框架上的一个能提供 http 服务的独立应用,所以框架结构此处从略。
7.2 核心类和接口
NS_DISPATCHER 是以 NETTY 框架为基础的,所以其核心类就是如下的几个协议处理器:
HttpDispatcherServerHandler:主要负责解析传入的 http 请求,并封装成对应的 java 对象交给 HttpRPCHandler 做进一步处理。
HttpRPCHandler:主要接收上一步封装好的 java 对象,并取出对应的请求参数、请求内容等,封装成系统内部传输用的协议对象,并可以以同步请求响应模式 / 异步发送模式将协议对象放入底层消息中间件。
7.3 配置方案
NS_DISPATCHER 启动会去找 ns_dispatcher.properties 文件,下面介绍配置的关键元素:
http.port:指定了 http 服务的监听端口。
dispatcher.pool.num:指定了 dispatcher 的并发线程数,dispatcher 的性能和这个参数有非常大的关系。
dispatcher.queuename:封装好的内部协议消息要放入的队列的名字,NS_DISPATCHER 也支持 https,所以,如果在 ns_dispatcher.properties 文件中有如下几个选项,那么 NS_DISPATCHER 也会启动对应的 https 服务。
ca.path:指明了可信任证书的路径。
key.path:指明了公钥的路径。
https.port:指明了 https 服务监听的端口。
八、NS_CONTROLLER 应用介绍
8.1 框架架构
NS_CONTROLLER 本质是建立在 NS_TRANSPORTER 和 NS_CHAINS 上的独立应用,核心就是 NS_TRANSPORTER 的架构加 NS_CHAINS 的辅助,故不再重复列举其架构。
8.2 核心类和接口
DefaultPublishCommand:这是 NS_CONTROLLER 对于 NS_CHAINS 的一个扩展,它支持同步发送消息,并等待消息的响应,并可以设置等待响应的超时时间。同时,还支持异步发送消息,不需要等待消息的响应。
8.3 配置方案
遵循 NS_TRANSPORTER 和 NS_CHAINS 的配置规则,所以不再赘述。注意:在 NS_CONTROLLER 中对于 NS_CHAINS 的配置做了一些功能扩展,主要是添加了 publish 的配置元素,这个随后可以提供配置模板。
九、项目部署
9.1 部署方案
如果要部署整个 ns4_frame 项目,请按照以下步骤进行:
部署 NS_DISPATCHER 项目:NS_DISPATCHER 项目是一个 Maven 项目,首先需要通过 mvn:package deploy 将整个项目打成一个 zip 包上传到服务器,然后解压成一个目录。在这个目录中,有如下几个子目录:bin、config、lib、logs。其中,bin 目录中包含了 DISPATCHER 的启动脚本;config 目录存放了 NS_DISPATCHER 必须的配置文件;lib 目录存放了 NS_DISPATCHER 所需要的所有 jar 包;logs 目录存放了所有 NS_DISPATCHER 打印的日志。
部署 NS_CONTROLLER 项目:NS_CONTROLLER 项目也是一个 Maven 项目,需要通过 mvn:package deploy 将整个项目打成一个 zip 包。目录结构同 NS_DISPATCHER 项目,此处不再赘述。
部署业务代码:业务代码请自行按照各个团队的规则部署。
十、运行日志
10.1 日志分类
ns4_frame 项目将日志大致分成了四类:
*fram.log:系统日志,属于整个 ns4_frame 底层系统内部的日志,包括系统的启动,线程的启动关闭等信息。
*biz.log:业务日志,所有业务相关的日志统统会被导向到这里。
*flow.log:消息流日志,这里记录了系统所有消息的流转信息。
*mq.log:这里记录所有对底层消息中间件进行操作的信息。
10.2 如何查看日志
业务报错:如果业务报错,基本所有的报错信息都会在 *biz.log 中查到。
消息流转: 如果是消息发送响应的问题,基本上在 *flow.log 中可以查到或者推断出相关的信息。
底层消息中间件交互: 如果消息流转无法推断出问题,或者无法查到对应的消息,就需要转到 *mq.log 中进行查询。
十一、其他
11.1 常⻅问题
ns4_frame 系统本质是一个以消息为通信机制的分布式系统,经常出现的问题分成以下两部分:
业务异常
由于业务本身是由底层 NS_TRANSPORTER 回调来执行的,当业务出现异常的时候,很可能由于没有合适的被 catch 到,从而被底层的 NS_TRANSPOTER 框架捕获。对于没有在 *biz.log 和 stdoout.log 中查找到的问题,可以去查看下 *flow.log 的日志,看是否出现了异常被底层 NS_TRANSPOTER 捕获了。
底层异常
有些情况,业务本身并没有出现问题,但是由于消息通信出现了问题,会导致业务没有执行,对于 这种情况我们需要首先从消息入口处即 NS_DISPATCHER 的 *flow.log 中查找到对应的 messageId,然后根据消息流转路径,一步步去对应的部署机器上查询。
内容来源:宜信技术学院