千万用户级别应用系统背后的SOA组件化容器
背景
在《我们的应用系统是如何支撑千万级别用户的》随笔中已经从“宏观”角度去介绍了整个应用系统的布局。组件化是整个系统由头到尾都始终坚持的一个设计原则,其中“SOA组件化容器”也是我们应用系统比较特别的一点。好东西肯定要分享,当然,这个好还只停留在自恋当中。
主题
上图为整个SOA容器(即WEB容器)的透析图。其中各个(黄色)组件的执行流程就是整条业务线程的执行流程。例如在我们应用系统中主要包括会话组件、安全拦截组件、业务验证组件、业务解析组件、业务服务组件、业务响应组件、日志组件等。也就是这些组件组成了整个业务线程的生命周期。接下来对SOA容器的各个组成部分进行一一介绍。
SOA组件配置文件
配置文件就是组件执行引擎的依赖,有点类似于Spring配置文件的概念,下面是配置文件的一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<soabean id=”session” class=”com.soa.session” order=”1”/>
<soabean id=”security” class=”com.soa. security” order=”2”/>
<soabean id=”validate” class=”com.soa.validate” order=”3”/>
<soabean id=”analysis” class=”com.soa.analysis” order=”4”/>
<soabean id=”service” class=”com.soa.service” order=”5”/>
<soabean id=”response” class=”com.soa.response” order=”6”/>
<soabean id=”logger” class=”com.soa.logger” order=”7”/>
其中soabean为SOA容器组件,id为组件的唯一标识,class为组件的类路径,order为组件的执行顺序。
SOA组件执行引擎
执行引擎就是整个SOA组件容器的发动机,容器初始化时需要加载SOA组件配置文件信息并通过反射生成各组件实例根据序号(order)依序存放在SOA组件单例队列容器里。当业务请求到达SOA容器时,执行引擎循环执行单例队列容器里面的组件。
/*伪代码*/
Loop singleSOABeanList: //循环组件单例队列容器
SOABean.handle(); //组件执行
SOA组件单例队列容器
这是一个存放着SOA组件实例的单例队列容器,队列的顺序依赖于配置文件的order值,按队列容器按order值升序排序,order值越小的组件越优先执行。
ThreadLoal(线程本地变量)
这是整个线程生命周期的本地变量,主要存储的是会话信息,而这个会话信息就是各组件解耦的关键所在,贯穿了所有组件的执行。会话信息主要包含着以下字段:
Class Session{
serssionId //会话标识
seviceName //请求服务标识,如SOA_001_001,可自定义业务规则
retCode //业务响应标识
retDesc //业务响应描述
isGone=true //线程主流程执行信号(true/false)
soaLogger //SOA容器组件日志跟踪器
reqJson //请求参数报文
respJson //响应参数报文
HTTPRequest实例
HTTPRespone实例
Userinfo //用户信息
……
}
此会话跟HTTP会话的作用是一样的,主要是维护用户不同请求的关联关系,同时还是各组件的解耦的关键所在。
SOA组件
上图中的黄色组件为各SOA容器的业务组件,业务组件需要实现SOA组件接口。
public interface SOAInterface {
public void handle();
}
组件是一个独立体,必须管理好自己所以异常信息,可通过会话实体记录和跟踪会话的运行情况。组件又分为主流程组件和辅助组件,主流程组件是通过会话的线程主流程执行信号(isGone)来跟踪主业务流程的执行情况,如果某主业务流程组件发生异常或业务失败,则通过session.setIsGone = false告诉后续的主流程组件不需要执行了:
/*主流程组件伪代码*/
Class SubGroup implement SOAInterface(){
try{
If(session.isGone){
operations
…
If(operation false){
session.setIsGone = false;
}
}
}catch(Exception e){
session.setExceptionLog;
session.setIsGone = false;
}
}
在我们应用中,主流程组件有会话组件、请求解析组件、业务处理组件、请求业务响应组件等。而辅助组件有黑名单拦截组件,日志组件等。辅助组件的异常不会导致整条业务线程的奔溃,遇到异常,不会对主线程的运行标识(isGone)进行干预,以免影响整条主业务线程的正常服务。例如黑名单拦截,如果就因为黑名单组件异常失败,而停止系统所有业务对外提供服务,那有点说不过去。其实说白了,还是根据自己业务而定。对于一些比较敏感的系统组件,可以通过实时异常监控组件去跟踪来弥补日志异常信息监控缓慢的缺点。例如可以利用“发布订阅系统”:
/*会话记录组件异常信息伪代码*/
session.setExceptionLog(){
ExceptionLog;
PubSubMonitor.publish(exceptioninfo); //通过发布订阅监控组件实时监控
}
总结
不难理解,各个组件就是线程栈的组成部分。SOA组件化容器把整个业务请求流程的各个组成部分都封装成组件化形式执行,例如会话解析、请求参数解析、业务逻辑处理、请求业务响应、日志记录等业务主要流程。有利于系统日后的扩展性和维护性。这个SOA容器其实有点类似于企业服务总线(ESB)的概念,也同样支持类似Spring的AOP功能,毕竟在一条垂直的业务线程上,可通过组件配置文件在随意一个横切面插入其他操作组件。无论SOA容器组件还是服务层(SOA容器业务逻辑处理组件)的业务服务组件,都是通过反射实现生产单例使用,也有点类似于Spring的IOC概念。在这里我主要还是提供想法和思路,只有思路是通用的,还是建议思路+具体业务的实现才能发挥系统的最大性能。DIY过程最好不要脱离组件化和简单化的设计原则,当然,定义=限制,不同人都有不同的可能,欢迎评论交流。