一 分布式服务框架诞生背景
分布式服务框架的诞生并非偶然,当服务发展到一定阶段之后,会面临很多新的挑战。包括新的商业模式,已有技术架构无法支撑业务快速发展等为了保证业务的快捷、健康发展,往往会产生新的技术架构来应对挑战,分布式服务框架的产生也符合这种技术发展规律。1 应用从集中式走向分布式随着业务的发展,应用的功能越来越多,打包、部署和新特性上线周期也变得越来越长,大流量、高并发的用户访问对后台服务的压力越来越大,我们只能通过不断增加硬件的方式来满足应用的低延时和高吞吐量。通过新增硬件的方式对应用进行扩容,可以暂时顶住高峰期大并发对系统的冲击,但仍有一些棘手问题无法通过扩容的方式解决:1)业务不断发展,应用规模日趋强大,大型复杂应用的开发维护成本高,部署效率低;应用数量膨胀,数据库连接数持续变高。2)代码复用是难题,由于公共模块都是进程内部的本地API调用,开发者经常会按需开发,这会导致大量功能类似甚至相同的API被重复开发。3)敏捷持续交付面临巨大挑战:业务和代码功能都太大,新增或修改一个功能难度很大。2到3周短期迭代不适用。大规模系统架构的设计一般原则就是尽可能地拆分,以达到更好的独立扩展与伸缩、更灵活的部署、更好的隔离和容错、更高的开发效率。
横向拆分和纵向拆分:纵向拆分:不同业务模块独立部署,CRM系统可以按照客户模块,产品模块,营销管理模块拆分,将复杂的业务拆分成相对独立、灵活的具体能力,由大变小、分而治之。横向拆分:将核心的、公共的业务拆分出来,通过分布式服务框架对业务进行服务化,消费者通过标准的契约来消费这些服务。服务提供者独立打包、部署。
分布式框架功能特性
尽管不同的分布式服务框架实现细节存在差异,功能特性也不尽相同,但是一些公共能力是必须具备的。下面我们对分布式服务框架的功能特性进行总结:1 服务订阅发布配置优化发布和引用服务功能(支持通过XML配置的方式发布和导入服务,降低对代码的侵入)服务自动发现机制功能(支持服务实时自动发现,由注册中心推送服务地址,消费者不需要配置服务提供者地址,服务地址透明化)服务在线注册和去注册(支持运行态注册新服务,也支持运行态取消某个服务的注册)2 服务路由默认提供随机路由、循环、基于权重的路由策略等(默认提供常用的路由策略,避免每个框架使用者都重复开发)粘滞连接(总是向同一个提供方发起请求,除非此提供方挂掉,再切换到另一台)路由定制(支持用户自定义路由策略,扩展平台功能)3 集群容错Failover(失败自动切换,当出现失败,重试其他服务器,通常用于读操作)Failback(失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作)Failfast(快速失败,只发起一次调用,失败立即报错)4 服务调用同步调用(消费者发起服务之后,同步阻塞等待服务端响应)异步调用(消费者发起服务调用之后,不阻塞立即返回,由服务端返回应答后异步通知消费者)并行调用(消费者同时对多个服务提供者批量发起服务调用请求,批量发起请求后集中等待应答)5 多协议私有协议(支持二进制等私有协议,支持私有协议定制和扩展)公有协议(体统Web Service等公有协议,用于外部服务对接)6 序列化方式二进制类序列化文本类序列化(支持JSON、XML等文本类型序列化方式,提升通用性和可读性)7 统一配置本地静态配置(安装部署修改一次,运行态不修改的配置,科研存放到本地配置文件中)基于配置中心的动态配置(运行态需要调整的参数,统一放到配置中心,修改之后统一下发,实时生效)分布式框架并非需要100%支持上述功能特性,具体问题具体分析。性能特性
高性能,低延时,性能线性增长可靠性由于网络故障、服务提供者故障等因素,会导致业务失败率增加,分布式服务框架需要具备很强的可靠性,来保证业务的成功率。1 服务注册中心服务健康状态检测(注册中心通过心跳监测服务提供者的存在,服务提供者宕机,注册中心将立即推送世间通知消费者)故障切换高HA(注册中心全部宕机,服务提供者和消费者仍能通过本地缓存通信)2 消除单点故障服务无状态(服务提供者无状态,任意一台宕机后,不影像使用)服务集群容错(只要集群中有一台机器的服务提供者可用,业务久不会中断)3 链路健壮性心跳检测(通过定时心跳监测链路是否可用)断连重连机制(链路断连后,根据客户端配置的重连策略定时重连)服务治理
1 服务运行态管控服务路由服务限流服务迁入迁出服务降级服务超时控制2 服务监控性能统计统计报表告警3 服务生命周期管理上线审批下线通知服务灰度发布4 故障快速定界定位分布式日志采集海量日志在线检索调用链可视化展示运行日子故障定位5 服务安全敏感服务的授权策略链路的安全防护分布式服务框架实际并没用标准,不同的业务场景会有不同的特性需求。
通信框架
单机版的本地方法调用变成远程服务调用之后,一个高性能的通信框架成为分布式服务框架必不可少的有机组成部分。长连接还是短连接绝大多数的分布式服务框架RPC框架,都推荐使用长连接进行内部通信,为什么选择长连接而部署短连接呢?1)相比于短连接,长连接更节省资源。如果每发送一条消息就要创建链路、发起握手认证、关闭链路释放资源,会损耗大量系统资源。长连接只在首次创建时或者链路断连重连才创建链路,链路创建成功之后服务提供者和消费者会通过业务消息和心跳维系链路,实现多消息复用同一个链路节省资源。2)远程通信时常态。服务化之后,本地API调用变成了远程服务调用大量本地方法演化成了跨进程通信,网络延时成为关键指标之一。相比与一次简单的服务调用,链路的重建通常耗时更多,这对大型业务系统时无法接受的。BIO还是NIO
对于高负载、高并发的网络应用,需要使用NIO的非阻塞模式进行开发自研还是选择开源的NIO框架
直接使用Java NIO类库比较麻烦:API繁杂,需要具备其他额外技能如Java多线程,Java NIO bug一般采用开源的框架如:Netty
序列化与反序列化
服务提供者和消费者通过网络进行通信,对象需要进行序列化和反序列化,常见的序列化和反序列化方式很多,如何选择是重点。几个关键概念
我们习惯将序列化称为编码,它将对象序列化为字节数组,用于网络传输、数据持久化或者其他用途。反序列化称为解码,把从网络、磁盘等读取的字节数组还原成原始对象。序列化与通信框架的关系序列化与通信框架不是强耦合的关系,通信框架提供的编码框架可以非常方便地支持用户通过扩展实现自定义的序列化格式。用户也可以在应用程序及其他位置实现对象的序列化和反序列化。序列化与通信协议的关系序列化与通信协议是解耦的,同一种通信协议可能由多种序列化方式承载,同一种序列化方式可以在不同协议里。是否需要支持多种序列化方式整体而言,序列化可以分为文本类和二进制类两种,不同的业务场景需求也不同,分布式服务框架面向的领域是多样化的,因此它的序列化/反序列化框架需要具备如下特性:默认支持多种常用序列化和反序列化的方式,文本类如XML/JSON,二进制如PB/Thrift等。序列化框架可扩展
功能设计
在设计序列化/反序列化框架的时候,我们需要从功能、跨语言支持、兼容性、性能等多个角度进行综合考量。功能丰富度(支持的数据结构种类要多)跨语言支持(Java默认的序列化不可以跨语言,即用Java默认的序列化之后,无法用其他语言反序列化,MessagePack PB Thrift Avro等都支持多语言)兼容性(数据结构向前兼容)性能(序列化之后码流大小、序列化/反序列化的速度、资源占用)扩展性设计Netty提供的编解码框架,可以非常快速的实现序列化/反序列化框架的扩展最佳实践接口的向前兼容性规范高并发下的稳定性