Stargate是基于SOA思想的RPC框架,总体架构概览
在一套完整的分布式服务环境中,总共有服务注册中心、服务监控中心、若干服务端、若干客户端四个角色。
- 服务注册中心:独立部署组件,为服务端提供服务注册服务,为客户端提供服务查找服务。Stargate中采用zookeeper集群作为服务注册中心;
- 服务监控中心:独立部署组件,为调用双方提供服务监控服务。Stargate中的监控中心通过redis存储监控日志;
- 服务端和客户端通过jar方式使用服务框架,由框架负责远程调用中的服务配置、负载均衡、容错控制、流量控制、服务监控和小流量路由等基础功能。
凤巢分布式服务框架 总体设计说明书
v0.5
系统名称 | 涅槃 |
项目负责人 | 郑嘉庆、刘涛 |
作者 | 郑嘉庆、刘涛 |
文档提交日期 | 2011-11-23 |
百度在线网络技术(北京)有限公司
( 版权所有 , 翻版必究 )
修订历史
版本号 | 作者 | 修订章节 | 修订原因 | 修订日期 |
0.1 | 郑嘉庆 | 所有 | 编写初稿 | 2011.11.23 |
0.2 | 郑嘉庆 | 增加5、6、7、8章 | 增加开发、测试、运维相关规范,项目进度考虑 | 2011.11.24 |
0.3 | 郑嘉庆 | 第2、3、4、5、6章 | 根据jx内部讨论意见修改:调整几个架构图,增加服务注册中心相关描述,去掉小流量需求,一些细节修改等 | 2011.11.28 |
0.4 | 郑嘉庆 | 所有 | 调整文档格式,调整细节,按模板格式增加10、11、12、13章 | 2011.11.30 |
0.5 | 郑嘉庆 | 3、8 | 调整系统部署结构图,新增第8章 | 2011.12.01 |
|
|
|
|
|
目 录
1 概述
1.1 文档目的
阐述分布式服务框架的相关功能点,总体技术架构和关键设计策略;
统一框架设计人员,包括开发、测试、运维同学的设计思路;
明确后续系统服务化的相关规范,如服务发布规范、系统组件分层规范、单测规范、系统发布规范等等;
1.2 术语 解释
名称 | 说明 |
面向服务架构/SOA | 通过服务的方式组织系统的功能模块,以达到服务单元的高内聚,服务之间的松耦合的架构设计 |
分布式服务框架 | 支持SOA服务治理的RPC远程调用方案,本文阐述的重点 |
| |
服务注册中心 | 在系统部署架构中作为一个第三方独立部署的目录服务,维护当前可用的服务列表和服务的订阅关系 |
服务提供方/提供方 | 提供服务的应用系统 |
服务消费方/消费方 | 调用某个远程服务的应用系统 |
客户端 | 站在服务注册中心的角度,服务提供方和服务消费方都属于注册中心的客户端 |
|
|
1.3 需求背景
目前凤巢业务端正在将原本烟囱式的系统架构向面向服务的分布式架构调整,为了更好的支持分布式系统间的服务调用和治理,势必需要一套服务框架作为底层支撑,统一管理分布式调用的相关功能,简化应用系统开发的复杂度,提升系统稳定性。
其次,百度广告平台的其它产品线也有向分布式转型的打算,如果能有一套较通用的服务框架,有助于广告平台技术体系的统一化,降低研发、维护成本。
1.4 设计目标
完成分布式服务框架第一个版本,提供分布式服务调用的基础功能,为ad-core、data-core的服务化提供良好支持;
服务框架在容量、可靠性等方面能支撑现有凤巢的业务规模,性能不会成为业务处理过程中的瓶颈;
框架自身具备良好扩展性,便于日后增加新功能;
2 需求概述
2.1 容器集成和配置管理
服务框架应该支持与现有Spring框架集成,便于应用系统接入服务框架。同时提供较为便捷的配置方式供RD配置服务的发布/查找,以及服务调用过程中的策略控制,如流量限制、负载均衡、重试策略等。
2.2 服务发布/查找
支持服务提供方动态地发布服务,服务消费方能够获取需要的服务地址列表,并实时感知服务端的上、下线,便于实时隔离故障机器、运维的平滑发布等。
2.3 远程通信
支持搞效、可靠的远程调用,并为服务消费方提供必要的调用方式选择,如同步调用、异步调用、异步回调等,支持服务提供方根据需求采用合适的通信方式,如多种通信协议、连接方式等。
2.4 调用策略控制
从负载均衡、调用超时时间控制、重试策略、并发流量控制等维度提供多种配置策略,由应用系统按照需求配置,以保证服务调用的成功率和可靠性。
负载均衡:
重试策略:
流量控制:
2.5 服务监控
对服务调用过程中的相关指标进行实时监控,并定期汇总统计,将汇总信息反馈给相关组件、供监控系统(Noah)产生监控图表。监控指标包括:单位时间并发量(流量)、失败次数、消费方平均调用耗时、服务方平均处理耗时、请求来源、峰值等等。
3 总体架构
3.1 系统整体架构
下面采用RUP4+1视图的方式,从不同侧面对系统整体架构进行描述:
3.1.1 逻辑视图:系统总体逻辑架构
下图从全局描述了使用分布式服务框架后的系统整体架构:服务端通过服务框架将服务发布到服务注册中心,消费端从服务注册中心查找需要的服务,消费端通过服务框架进行远程服务调用。
(服务框架总体概念架构)
服务框架以嵌入消费端、服务端系统的方式运行,统一负责系统间调用的相关功能,如服务的发布/查找、调用策略的控制、服务的监控、远程通讯支持等;
服务注册中心作为一项独立的目录服务,负责维护服务端的服务列表, 供消费端查询、监听最新的服务列表;
后续如果有较完善的监控需求,可增加独立的监控中心,负责定期采集各应用系统上的本地监控数据,汇总为监控图表、触发报警等;
3.1.2 物理视图:系统总体部署结构
(系统分布式部署后的总体部署结构)
前端流量转发保持不变,仍然使用transmit转发,如果有小流量需求,则独立部署一套小流量web层、biz层、core层应用;
web层应用保持不变,仍然使用apache作为web容器,tomcat作为应用容器;biz层和core层不需要部署apache;
web、biz、core层应用之间通过服务注册中心维护服务的发布、订阅关系,通过socket进行服务调用,由分布式服务框架负责负载均衡,不需要单独部署BVS;
web、biz、core层应用集群内部均为无状态设计,同个集群内的机器不存在相互调用;服务注册中心集群为有状态设计,集群内部通过tcp长连接维持机器间的数据同步;
core层应用通过JDBC调用数据库(biz层应用根据需求,也可以有自己的数据库);
core层应用与后端server的交互保持不变,仍然通过BVS做负载均衡;
各层级的应用均为跨机房共用,即A机房的web可以连接A、B、C … 机房的服务注册中心,从而调用A、B、C … 机房的biz层;
3.1.3 开发视图:服务框架内部组件
服务框架内部主要包含如下几个组件:
服务监控 :监控服务调用过程中的调用量、并发量、失败次数、耗时、成功/失败等信息,(后续可做)汇总产生监控图表,将统计信息反馈给相关组件等;
配置管理 :支持与spring框架的集成,解析服务配置(xml ) 并初始化服务代理对象;
服务代理 : 一项服务在服务提供方、服务消费方的代理对象,维护该服务在本地的运行状态,桥接底层通信请求与系统本地业务Bean的调用关系;
服务注册/查找 :负责将服务发布到注册中心,查找/监听某服务的最新的地址列表,同时对服务订阅关系做相关权限控制,权限控制主要针对服务接口的可见性;
调用策略控制 :服务调用过程中的健壮性控制,如负载均衡、并发流量、超时限制、重试策略等;
远程通信支持 :通过集成通信框架以完成远程调用,包括通信协议集成、encode/decode、序列化/反序列化等;
(*注:上述的“组件”目前只是通过package划分,后续有必要再调整为OSGi组件)
3.1.4 过程视图:内部组件调用关系
下面从‘服务端系统启动’、‘消费端系统启动’、‘消费端对服务端发起一次服务调用’三个过程阐述该过程中服务框架内部各组件间的调用关系:
服务提供方启动 过程中涉及的组件以及调用关系如下图:
服务消费方启动 过程中涉及的组件以及调用关系如下图:
服务消费方调用远程服务 过程中涉及的组件以及调用关系如下图:
服务提供方处理远程请求 过程中涉及的组件以及调用关系如下图:
3.1.5 设计考量:系统健壮性
服务消费方 :单台故障不影响集群中其它服务器,不影响注册中心和服务提供方;
服务提供方 :单台宕机时注册中心会将最新服务列表推送给消费方,不需人工介入;如果是由于某些原因hang住(如高负载、连接池满等),注册中心不能识别该故障时,由消费方自身的超时限制、重试策略保证调用质量。注意:这种情况下消费方的请求还是可能重复调用到故障的服务方,需要OP尽快移除/修复故障机器;
服务注册中心 :注册中心之间的数据实时同步,单台宕机不影响数据的一致性;客户端对注册中心的长连接带有重试功能,当某台注册中心宕机或网络连接中断时,客户端自动发起重连;
3.1.6 设计考量:系统伸缩性
服务消费方 :理论上客户端支持线性扩容,扩容需注意对服务注册中心增加的长连接压力
服务提供方 :理论上服务端支持线性扩容,扩容需注意对服务注册中心增加的长连接压力
服务注册中心 :可通过扩容以支持更大的客户端连接
数据库 :数据库扩容不属于本框架职责,但需要注意应用系统的扩容可能增加数据库连接数
3.1.7 设计考量:系统性能
系统分布式部署之后,组件之间的远程通信势必带来一定的性能损耗。不过从现有场景看,性能瓶颈一般出在大数据量的DB操作,或者不合理的连接线程池设置,也就是应用系统的具体业务场景上。对于大数据量的DB操作,可从业务上考虑限制每次的操作笔数、或者改为后台异步处理,对于实时性要求不高的请求,可考虑改为异步远程调用。
另外服务框架提供性能日志的记录,可供后续分析相关瓶颈问题。
3.2 系统领域模型
服务(Service): 服务端对外暴露的一个接口称为一个服务,包括所在应用系统的名称,服务名(完整包路径名),服务版本,支持的协议,本地IP地址和端口等信息,一个具体的服务在服务端可描述为:
<完整包路劲 . 服务接口名:服务版本 @ 采用协议 # IP地址:端口>,比如:
com.baidu.fc.adcore. service. ideaservice :1.0@NETTY#138.1.1.56:8080
服务注册中心通过树形目录维护服务列表,目录结构如下图:
app :提供服务的应用系统名称;
service :该系统提供的一项服务(粒度精确到interface),一个系统可提供多项服务;
version :该服务所属版本;(对不同版本服务的定义是:接口一样,实现逻辑不同);
protocol :该服务支持的通信协议,默认为NETTY;
permit :控制该服务对那些消费端可见;
address :该服务当前可用的地址列表,包含机器的ip和port;
3.3 依赖技术
3.3.1 Netty
服务框架通信层采用Netty作为默认通信框架,处理实际的远程调用。
3.3.2 p rotostuff
服务框架采用protostuff作为默认的对象序列化工具。
3.3.3 Spring
凤巢业务端系统都是采用spring作为开发框架,服务框架默认支持和spring的集成。
3.3.1 Zookeeper
服务注册中心直接采用Zookeeper维护服务列表、与客户端通讯等。由Zookeeper保证客户端请求的顺序执行、更新操作的原子性、服务列表的全局一致性。
3.3.1 Jdk1.6
考虑 J dk1.6较1.5在内存分配、GC效率等方面有一定的提升,服务框架基于jdk1.6最新版本开发,生产环境/测试环境也采用jdk1.6最新版本(注意:1.6早期版本存在一些bug,必须使用最新版)
4 关键模块设计
4.1 基于标签的服务配置
服务框架通过自定义.xsd语法的方式定制一套服务标签,包含服务发布<bsf:service>,服务引用<bsf:reference>等,在容器启动时加载服务标签,并初始化相关服务代理对象。
具体的标签规则请见:< 分布式服务框架_标签配置参考手册_v0.1.xlsx > (详设中完成)
4.2 基于zookeeper的服务发布/查找
服务的发布/查找通过服务注册中心(zookeeper)统一管理。
4.3 可配置的调用策略
框架默认提供多种调用策略,由RD针对每个服务请求的具体情况配置策略,比如<bsf:reference loadbalance= ” RR ” timeout= ” 1000 ” retrytiems= ”3 ” maxconnections= ” 10 ” >
4.4 基于切面的服务监控
框架内部通过拦截器的方式,在服务调用前、调用后两阶段埋点记录每次服务请求的调用情况。同时通过后台定时任务定期汇总监控信息。
4.5 通信协议支持
本期优先支持Netty作为通信框架,protostuff做序列化。
4.6 对新Driver基础功能的支持
对于Driver需要使用的如流量控制、超时控制、调用日志记录等功能,考虑由服务框架以Jar包的方式提供。
5 开发约束
5.1 服务发布
服务端系统将服务接口 (IF)和相关传输对象(DTO)打成Jar包存放到共享仓库(Maven Repository,按系统名存放在com.baidu.fengchao目录下),需要依赖该服务的消费端系统在编译时自行从仓库下载Jar包(依赖关系写在pom.xml中,maven编译时自动下载);
服务Jar包统一以 <所在系统.版本号> 命名,如 fc-adcore.2.2.22.jar;
建议服务Jar包版本号与项目版本 (也就是SVN的分支版本,最终都会合并到主干)区分开,对于没有接口变更的项目,不必变更Jar版本号;
在系统升级时,要求服务Jar包必须向下兼容,对于无法兼容的服务,则另开新接口或方法;
5.2 组件分层
对于后续基于分布式环境构建的应用系统,上图提供了一个可参考的内部组件分层结构。对于web型应用,可采用web、biz、common三层结构,对于core型应用,可采用service、biz、core(可选)、common四层结构。各组件的含义如下:
web.module:web层的功能模块,按功能划分;
web.home:web层的公用模块,负责安全校验、公共错误页面、校验码等;
service. façade :系统对外暴露的服务接口,可打成服务Jar包发布到共享仓库;
service.impl:service的具体实现,只负责桥接作用,通过调用biz模块处理具体请求;
biz.module:业务功能模块,按功能划分;
core.service:领域服务,系统核心的、原子粒度的业务服务;
core.model:领域模型,系统的业务模型、规则枚举等;
common.dal:data access layer,负责访问数据库相关操作;
common.sal:service access layer,负责调用外部服务相关操作;
common.util:系统内部公用的工具类库,同时引入系统需要的第三方Jar包;
(*注:上述是一个比较全面的分层,有点是职责明确,缺点层次之间涉及多次对象转换、与现有FC的代码结构存在一些冲突,本期项目先在ad-core系统中尝试组件分层,通过实践确定适合FC自己的方案,后续再形成统一规范推广到其它应用系统 )
5.3 单元测试
应用系统的业务逻辑层(biz、core)必须编写单元测试,web和common层根据实际情况确定;
单元测试按照bundle存放,如下图所示,测试代码放在src/test/java,测试配置文件放在src/test/resources,严禁将测试代码、测试用的配置放在生产代码目录(src/main);
测试用例以<用例编号_被测方法名_测试场景_预期结果>命名,这样的优势是通过方法名即可清晰看到测试场景,并且采用中文易于表述。对于太过庞大的业务代码,建议拆分业务代码,或者以<方法>为粒度组织测试类。测试方法命名示例:
测试覆盖率建议达到行覆盖60%以上,eclipse可安装EclEmma等统计插件(不同统计插件的结果可能会有偏差);
5.4 系统日志
服务框架日志
服务框架日志主要包括<服务订阅日志>、<服务调用日志>、<请求处理日志>、<调用跟踪日志>、<错误日志>五项,由服务框架自己负责,RD/QA只要根据需要配置对应日志级别即可,建议生产环境配置info,测试环境配置debug。日志保存周期15天。
服务订阅日志,info,记录系统启动时发布的服务、获取的服务列表,已经后续注册中心实时推送的服务列表;
服务调用日志,info,记录每次服务调用的基本信息,同时可供监控服务采集;日志格式:<调用时间:应用名.服务名.方法名,成功/失败,耗时>,比如:
2011-11-11 10:00:00,987 : adcore.addIdeaService.add,Y,10ms(可根据需要补充)
请求处理日志:info,记录每次服务请求的基本信息,同时可供监控服务采集;日志格式:<调用时间:来源应用,服务名.方法名,成功/失败,耗时>,比如
2011-11-11 10:00:00,987 : adbiz,addIdeaService.add,Y,8ms(可根据需要补充)
调用跟踪日志,debug,记录每次服务调用的详细信息以及调用堆栈,由于传递、记录的信息量较大,建议生产环境不要打开这份日志;
错误日志,error,框架处理过程中的报错信息,包括错误原因和异常堆栈信息;
应用系统日志
应用系统日志由RD根据需要设置,建议设置如下几份日志:
错误日志,error,应用系统处理过程中的错误信息以及异常堆栈;
业务日志,info,可再按照功能模块划分为多份日志,记录业务处理相关信息;
数据库访问日志,info,记录每次数据库访问的基本信息,以统一的格式打印,后续也可供监控服务统一采集,格式:<调用时间:数据库名.方法名,成功/失败,耗时>
5.5 公用Jar管理
对于多个系统都会用到的基础类库,可打成公共Jar包发布到Maven仓库,由需要的应用系统自行导入;
公共Jar也纳入服务Jar管理,每次升级需向下兼容,并制定统一版本号;
5.6 M aven仓库管理
按现行方式管理。
6 测试分析
6.1 测试环境搭建
在系统转型成分布式之后,测试环境可以考虑部署为多套:
稳定环境:包含所有应用系统和注册中心,保持线上最新版本代码,保证各系统功能24H稳定可用;
QA专用环境:包含所有系统、注册中心和QA专用的测试数据库,RD无权在上面增删改数据,用于回归测试; (也可和稳定环境共用,QA专用测试账号)
分支环境:用于日常项目开发,按项目需求分配必要的系统,其它系统统一依赖稳定环境;分支环境的系统可通过配置直连目标服务,而不通过注册中心(避免与稳定环境中的注册中心产生冲突);
分支环境可能出现的几种情况:
场景 | 应对 |
web改动,biz、core不变 | 申请新web,依赖稳定环境biz、core |
web、biz改动,core不变 | 申请新web、biz,依赖稳定环境core |
core改动,web、biz不变 | 需要一套web、biz、core |
biz改动、web、core不变 | 需要web、biz,依赖稳定环境core |
|
|
6.2 集成测试
集成测试建议由QA负责,按服务的粒度编写各个应用系统的测试用例。被测系统对外的依赖不做专门的屏蔽,仍然调用现有测试环境中对应的服务。测试环境分配请见上述6.1小节。
6.3 单元测试
建议应用系统的单元测试都由RD负责,详细请见5.3节
6.4 持续集成和代码质量度分析
由RD完成单测,QA部署持续集成环境,每周定时更新到Hadson产出持续集成报告、sonar产出代码质量度报告。对于较大规模的项目,可单独部署项目专用的Hadson和Sonar,供项目组使用。
6.5 测试环境错误定位
服务框架在系统调用过程中会传递上下文信息,上下文记录了每次请求调用的调用方、被调用方详细信息(应用名、服务名、版本、IP以及端口等),通过debug日志打印。在测试环境可通过debug日志观察服务调用的具体执行情况已经错误位置。
7 运维分析
7.1 系统发布流程
对于有服务依赖关系的系统,遵循服务方先发、消费方后发的原则,比如 core biz web;服务的下线则顺序相反。
同个集群的服务通过逐台重启的方式发布。对于服务方,在重启时会自动断开与注册中心的连接、启动后自动重连,注册中心实时将可用服务列表推送到消费方,以保证消费方不会调用到正在发布的服务方。对于消费方,启动后自动连接注册中心,获取当前可用的服务方列 表。
在系统规划时应避免存在系统间的循环依赖,比如 A B C A ,或者 A B。
7.2 跨机房调用
应用系统、服务注册中心的部署都支持跨机房调用。
7.3 服务视图
服务注册中心统一维护了当前所有服务的依赖关系,可通过注册中心提供的页面查看当前服务视图信息。
7.4 系统故障维护
对于系统中故障机器的维护,请见 <3.1.5 设计考虑 : 系统健壮性>
8 存储设计
分布式服务框架本身不设计数据的持久性存储。
9 项目相关
9.1 项目时间安排
由于分布式服务框架会提供给adcore、datacore、新driver使用,所以服务框架需要再其它几个项目之前完成。
9.2 开发工作量
工作量估算在详细设计之后给出。
10 非功能性分析
10.1 性能分析
请见3.1.7节 设计考量:系统性能。
10.2 可测试性分析
请见第六章:测试分析。
10.3 容量分析
请见3.1.6 设计考量:系统伸缩性。
10.4 容错性分析
服务提供端 :服务端需要从业务层面对所提供的服务做幂等控制、并发控制、流量控制、超时控制,以应对消费端请求重发、并发调用、超量请求、请求hang住等场景;其中流量控制、超时限制可直接通过服务框架配置;
服务消费端: 消费端需要从业务层面对所调用的服务做超时控制、重试控制、流量控制,以应对请求长时间无响应、失败重试、并发流量过大等场景;
10.5 健壮性分析
IDC故障: 由于服务端为无状态设计、服务注册中心自身保证数据一致性,单IDC故障时注册中心自动将请求转发到其它IDC,多IDC故障需要运维工程师紧急修复;
网络故障 :局部的网络故障可由服务注册中心自动剔除故障机器,大面积故障需要网络工程师紧急修复;
服务框架 :服务框架按系统粒度分配服务线程池,理论上某系统的故障不影响其它系统的服务调用;对于单次远程调用的质量依赖于Netty,通过Netty自身保证通信可靠性;
应用系统 :服务框架为应用系统提供多种调用策略,由应用系统根据自身需求选用合适的策略,以提高服务调用的成功率;
11 风险评估及对其它系统影响(可选)
11.1 已知的或可预知的风险
在这里加上已经知道的或可能会发生的风险,包括技术、业务等方面。最好针对每个风险,列出相应的应对措施。
11.2 与其它系统可能的影响
在“ 4.1 ”中描述了该系统与其它系统的依赖关系。在这里描述这些依赖关系可能带来的影响。包括本系统对其它系统可能造成的影响以及其它系统可能给本系统造成的影响两个方面。
12 技术委员会审核意见
No | 问题描述 | 提出人 | 处理方式 / 说明 | 状态 |
1 |
|
|
| Open |
2 |
|
|
| Close |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这里由技术委员会加入对以上系统设计的意见和建议 , 方案的最终确定需要技术委员会的通过
13 设计评审意见
No | 问题描述 | 提出人 | 处理方式 / 说明 | 状态 |
1 |
|
|
| Open |
2 |
|
|
| Close |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这里累计记录和跟踪该文档评审时发现的问题。
14 附件及参考资料
填写文档相关的附件或参考资料。若是不常变更的文档(比如调研报告),建议以对象方式插入到该文档中。如是经常变更的文档(比如接口文档),建议在此列出文件名即可。
凤巢分布式服务框架 详细设计说明书
v0.1
系统名称 | 涅槃 |
项目负责人 | 郑嘉庆、刘涛 |
作者 | 郑嘉庆、刘涛 |
文档提交日期 | 2011-12-01 |
百度在线网络技术(北京)有限公司
( 版权所有 , 翻版必究 )
修订历史
版本号 | 作者 | 修订章节 | 修订原因 | 修订日期 |
0.1 | 郑嘉庆、刘涛 | 全部 | 初稿 | 2011.12.01 |
0.2 | 郑嘉庆 | 2 | 更新名词解释 | 2012.1.11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
目 录
1 背景
请见<分布式服务框架_总体设计> 1.3节 需求背景。
2 名词解释
名称 | 说明 |
R egistry | 服务注册中心 |
M onitor | 服务监控中心 |
S ervice | 服务提供方对外发布的一项服务 |
R eference | 服务消费方引用的一项服务 |
Beam | BSF默认支持的通信协议,其内部使用Netty作为通信框架 |
B inding | 绑定,每个发布出去的服务都会绑定一个或多个通信协议,比如:binding.beam – 表示该服务是绑定beam协议发布的 |
|
|
|
|
其它术语请见<分布式服务框架_总体设计> 1.2节 术语解释
3 设计目标
3.1 实现的功能
相关功能请见<分布式服务框架_总体设计> 第4章相关功能简述。
3.2 设计的性能指标
4 系统环境(可选)
4.1 假设及与其它模块联系
4.2 相关软件及硬件
4.3 模块限制
4.4 数据规模估计
5 设计思路及折衷
服务框架总体上采用“黑板模式”设计:服务注册中心充当系统中的黑板,服务提供方将服务发布到注册中心,服务消费方从注册中心提取服务地址。该设计保证了服务提供方、消费方集群内部为无状态设计,理论上支持集群的线性扩容,同时单机的宕机不影响整体可靠性。另外注册中心与服务提供方、消费方之间的信息更新采用“推”的方式,如果注册中心故障,相关的信息仍然缓存在客户端的内存中,不影响系统正常运行。当然采用该模式后,需要对注册中心独立部署,在测试/运维上增加了一定复杂度。 (监控中心与之类似)
服务框架内部按照功能划分为多个独立的子模块(jar包),子模块间通过抽象接口依赖,以求在子模块层面能做到开闭原则,即某个子模块的升级不影响其它模块。服务提供方、消费方可根据自身需求import必须的jar包,而不必import整个服务框架。另外,像DR-Driver这类只需要一些通用功能、但不使用服务框架的模块,也可只import需要的jar。
6 模块设计
6.1 模块流程图及说明
服务框架内部主要包含如下几个组件(子模块):
服务监控 :监控服务调用过程中的调用量、并发量、失败次数、耗时、成功/失败等信息,(后续可做)汇总产生监控图表,将统计信息反馈给相关组件等;
配置管理 :支持与spring框架的集成,解析服务配置(xml ) 并初始化服务代理对象;
服务代理 : 一项服务在服务提供方、服务消费方的代理对象,维护该服务在本地的运行状态,桥接底层通信请求与系统本地业务Bean的调用关系;
服务注册/查找 :负责将服务发布到注册中心,查找/监听某服务的最新的地址列表,同时对服务订阅关系做相关权限控制,权限控制主要针对服务接口的可见性;
调用策略控制 :服务调用过程中的健壮性控制,如负载均衡、并发流量、超时限制、重试策略等;
远程通信支持 :通过集成通信框架以完成远程调用,包括通信协议集成、encode/decode、序列化/反序列化等;
下面从‘服务端系统启动’、‘消费端系统启动’、‘消费端对服务端发起一次服务调用’三个过程阐述该过程中服务框架内部各组件间的调用关系:
服务提供方启动 过程中涉及的组件以及调用关系如下图:
服务消费方启动 过程中涉及的组件以及调用关系如下图:
服务消费方调用远程服务 过程中涉及的组件以及调用关系如下图:
服务提供方处理远程请求 过程中涉及的组件以及调用关系如下图:
6.2 数据结构及说明
服务框架主要围绕“服务”进行封装, 注册中心的数据结构请见6.9.3.1节
6.3 算法描述(可选)
不涉及复杂算法
6.4 与其它模块的接口
服务框架对应用系统(如adcore)提供服务标签的配置,不存在直接的api接口调用
6.5 异常处理
当处理出错时,服务框架统一以如下异常反馈给应用系统
异常情况 | 异常类 | 处理方式 |
服务配置异常 | ServiceValidationException | 系统启动异常,需修复配置上的错误 |
服务绑定异常 | BindingException | 系统启动异常,需检查服务协议是否可用 |
服务调用异常 | ServiceRuntimeException | 运行期异常,应用系统根据自身情况选择是否重试 |
6.6 边界值说明
6.7 配置项说明
6.8 测试考虑
请见<分布式服务框架_总体设计> 第6章 测试分析
6.9 各子模块设计(可选 )
6.9.1 容器集成
服务框架通过自定义xsd标签的方式与现有spring容器集成。服务框架按spring的约定在META-INF目录下通过spring.schemas和spring.handlers指定自定义的xsd文档和标签处理器路径。容器启动后由spring调用对应的标签处理器解析服务标签。
容器启动时服务标签的总体解析过程如下:
容器初始化时 S pring通过spring.schemas/spring.handlers识别自定义xsd文档的语法规则和标签处理器,并初始化相关的标签解析器;
当需要解析自定义标签时,spring调用自定义标签处理器进行解析,解析器完成标签对象的初始化后统一给spring返回BeanDefinition对象;
对于服务标签的定义,主要考虑如下原则:
服务标签的配置尽量简单, 必要时 也可由RD定制相关高级配置项;
提供较好的扩展性,日后新增标签、属性时不影响现有系统的改动;
约定优于配置,而与显而易见的配置项可由框架默认处理,不必手工指定,比如服务Bean的beanID即为服务接口名(首字母小写)等等;
服务标签定义举例:
服务提供方以默认规则发布一项服务:
| ||
服务名称 | ||
本地引用 | ideaQueryService | |
服务版本 | 1.0.0【默认】 | |
服务分组 | normal【默认】 | |
通信协议 | beam | |
| 最大并发数 | 10线程【默认】 |
| 超时时间 | 30s【默认】 |
服务提供方自定义一项服务:
| ||||
服务名称 | com.baidu.fc.adcore.services.IndeaQueryService | |||
本地引用 | ideaQueryService | |||
服务版本 | 1.0.2 | |||
服务分组 | normal | |||
通信协议 | beam | |||
| 最大并发数 | 5线程 | ||
| 超时时间 | 8s | ||
| ws | |||
| 最大并发数 | 10 线程 【默认】 | ||
| 超时时间 | 30s 【默认】 | ||
| rest | |||
| 最大并发数 | 10 线程 【默认】 | ||
| 超时时间 | 30s 【默认】 |
服务消费方以默认规则引用一项服务:
| ||
本地beanID | ideaQueryService | |
引用服务 | com.baidu.fc.adcore.services.IndeaQueryService | |
服务版本 | 1.0.0 【默认】 | |
服务分组 | normal 【默认】 | |
通信协议 | beam | |
| 最大并发数 | 10线程 【默认】 |
| 单次调用超时时间 | 30s 【默认】 |
| 负载均衡策略 | 随机策略 【默认】 |
| 重试策略 | 失败即报错 【默认】 |
| 重试次数 | 0 【默认】 |
| 测试环境地址 | 无 【默认】 |
| 是否异步调用 | 否 【默认】 |
服务消费方自定义引用一项服务:
| ||
本地beanID | ideaQueryService | |
引用服务 | com.baidu.fc.adcore.services.IndeaQueryService | |
服务版本 | 1.0.2 | |
服务分组 | normal | |
通信协议 | B eam | |
| 最大并发数 | 10线程 |
| 单次调用超时时间 | 12秒 |
| 负载均衡策略 | 随机策略 |
| 重试策略 | 报错后重试 |
| 重试次数 | 2 |
| 测试环境地址 | 配置文件中指定 |
| 是否异步调用 | 否 |
具体的标签配置规则请见:< 分布式服务框架_标签配置参考手册 >
6.9.2 本地服务代理
服务框架通过本地代理对象维持本地bean和远程服务的映射关系和状态:
引用代理代表一项远程服务,记录该服务当前的服务地址和协议、采用的调用策略、调用次数等统计信息,在消费端本地bean发起调用远程调用时,封装调用请求并调用服务端;
服务代理代表一项服务,记录该服务当前的负载状态、请求来源等信息,并根据请求调用服务方本地bean进行处理;
引用代理的处理逻辑如下:
如果调用失败,服务框架将错误信息反馈给应用系统,异常类型为 ServiceRuntimeExcepion,应用系统通过异常的错误码识别错误类型;
需要注意,服务标签中配置的超时时间只限制单次的服务调用,如果框架进行重试,总耗时有可能超过服务标签中预定的时间;
服务代理的处理逻辑如下:
6.9.3 服务发布/查找
6.9.3.1 服务提供方的发布
分布式服务框架基于ZooKeeper实现服务的发布与查找服务,无需实现独立的注册中心服务.
在分布式服务框架中的服务提供方采用如下四个部分的描述进行唯一标识:
- 服务位置 ServiceRoot
标识在Zookeeper中的起始位置,用于区别其他类型服务,或者测试环境
- 服务分组 ServiceGroup
类似名字空间,可以映射为Java的Package
- 服务名称 ServiceName
服务提供方的名字,可以映射为Java的Class
- 服务版本 ServiceVersion
服务提供方的版本,分布式服务框架中允许多个版本的服务并存
每个服务提供方允许有多个实例存在,每个实例是在一个独立的jvm中运行,多个实例既可以存在于一台物理机器上,也可以存在于多台物理机器上.
服务在ZooKeeper中的结构如下
如图所示,标识信息分为四级目录依次存放,最后一级节点的Node001表示服务的实例,节点上将存放服务实例的host,端口,负载等实例相关的描述信息,以json格式表示
每个服务提供方的实例启动后,将通过服务代理根据自身的标识信息,在Zookeeper中相应位置注册为Node节点,并将自身的描述信息写入,从未完成服务的发布过程.
服务提供方的权限控制基于简单的用户名密码实现,采用Zookeeper的ACL来控制节点的可读状态.
6.9.3.2 服务消费方对服务的查找
服务消费方使用服务时,需要具备两个条件:
- 服务标识信息
即基本的标识:服务位置+服务分组+服务名称+服务版本,以及额外的权限信息等
- 服务接口
服务消费方的代理在启动时,会将自身作为一个Zookeeper Watcher注册到服务标识信息的目录上,获取该目录上服务提供方实例的信息,形成一个列表,并保持监听,当服务实例发生变化(启动或者退出,负载变化),也能实时更新这个列表.
根据这个列表的信息,服务消费方就可以查询到服务的相关信息,并根据自身的调用策略选择列表中的一个实例,发起请求并获得回应.
6.9.4 调用策略控制
6.9.4.1 负载均衡
负载均衡的核心接口是从服务的一组实例中选择出一个可用实例.
分布式服务框架支持多种均衡策略,包括:
- 随机访问
从一组实例中随机选择一个实例
- 轮询
依次按序从一组实例中选出一个实例
- 最小负载优先
选择负载最小的实例,负载因子由服务实例负责更新到Zookeeper中
6.9.4.2 错误处理
分布式服务框架中的服务消费方在调用服务提供方发生系统异常和网络异常时,可以提供以下几种重试策略:
- 错误直接返回
将异常信息作为结果直接返回
- 固定重试
遇到异常信息时,根据配置的重试次数进行反复尝试
- 切换重试
遇到异常信息时,重新通过负载均衡策略,找寻其他实例进行重试
另一方面,服务实例在注册到Zookeeper中采用Sequence Ephermal节点方式,当服务实例出现异常关闭时,Zookeeper会自动删除节点信息.从而做到节点的自动剔除.
当消费方在调用过程中,服务提供方发生未知异常或者严重超时,导致消费方无法得知错误情况时,提供超时机制,超过设置时间返回超时错误
6.9.4.3 并发流量控制
服务提供方的实例可以配置并发调用数阈值,有服务实例的代理负责当前调用数更新,消费方在负载均衡时会获取服务实例的并发调用数和阈值,发现并发调用数大于等于阈值时,会认为该服务实例不可用,从而实现并发的流量控制
6.9.4.4 核心接口描述
6.9.5 通信协议集成
6.9.5.1 通信实现
分布式服务框架采用RPC方式进行服务提供方和消费方通信,RPC通信方式如下所示:
分布式服务框架采用Netty作为通信层,采用nio异步方式进行通信.保证通信的高性能
6.9.5.2 序列化协议
分布式服务框架采用Protostuff进行传输过程Java对象的序列化.
6.9.6 服务监控
服务框架通过在服务调用的前、后过程中安插过滤器,对服务调用情况做相关日志记录,并通过定时的监控采集器将日志汇总到监控中心。
7 风险评估及对其它系统/模块影响(可选)
7.1 已知的或可预知的风险
在这里加上已经知道的或可能会发生的风险,包括技术、业务等方面。最好针对每个风险,列出相应的应对措施。
7.2 与其它系统/模块可能的影响
在“ 4.1 ”中描述了该模块与其它模块的依赖关系。在这里描述这些依赖关系可能带来的影响。包括本模块对其它模块可能造成的影响以及其它模块可能给本模块造成的影响两个方面。
8 设计及评审意见
No | 问题描述 | 提出人 | 处理方式/说明 | 状态 |
1 |
|
|
| Open |
2 |
|
|
| Close |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 附件及参考资料
填写文档相关的附件或参考资料。若是不常变更的文档(比如调研报告),建议以对象方式插入到该文档中。如是经常变更的文档(比如接口文档),建议在此列出文件名即可。