0
点赞
收藏
分享

微信扫一扫

分布式服务框架学习笔记2 常用的分布式服务框架 与 通信框架选择

传统垂直架构改造的核心就是要对应用进行服务化,服务化改造使用到的核心技术就是分布式服务框架。

分布式服务框架演进

应用从集中式走向分布式

大规模系统架构的设计一般原则就是尽可能地拆分,以达到更好的独立扩展与伸缩、更灵活的部署、更好的隔离和容错、更高的开发效率。
具体的拆分策略大体上可以分为横向拆分和纵向拆分。

纵向拆分

通过对业务进行梳理,根据业务的特性把应用拆开,不同的业务模块独立部署。

横向拆分

将核心的、公共的业务拆分出来,通过分布式服务框架对业务进行服务化,消费者通过标准的契约来消费这些服务。
服务提供者独立打包、部署和演进,与消费者解耦。

服务治理

主要诉求:
1. 生命周期管理
2. 服务容量规划
3. 运行期治理
4. 服务安全
典型的SOA服务治理生命周期如下图所示:
- 计划:确定服务治理的重点
- 定义:定义服务治理模型
- 启用:实现并实施服务治理
- 度量:根据实施效果,改进服务治理模型

业界分布式服务框架介绍

目前开源的有:
- 阿里的Dubbo
- 当当网基于Dubbo增强而来的DubboX
非开源的分布式服务框架,在移动互联网领域主要有:
- 淘宝的HSF
- 亚马逊内部使用的分布式服务框架Coral Service
电信行业主要有
- 华为软件开发的分布式服务框架DSF

分布式服务框架设计

架构原理

通常,分布式服务框架的框架可以抽象为三层
1. RPC层
2. Filter Chain层:服务调用职责链,提供多种服务调用切面供框架自身和使用者扩展,例如负载均衡、服务调用性能统计、服务调用完成通知机制、失败重发等
3. Service层:主要包括Java动态代理,消费得使用,主要用于将服务提供者的接口封装成远程服务调用;Java反射,服务提供者使用,根据消费者请求消息中的接口名、方法名、参数列表反射调用服务提供者的接口本地实现类。再向上就是业务的服务接口定义和实现类,对于使用Spring配置化开发的就是Spring Bean,服务由业务来实现,平台负责将业务接口发布成远程服务。

从功能角度看,分布式服务框架通常支包含另外两个重要功能:服务治理中心和服务注册中心,业务需求不同,具体实现细节也存在很大差异。以服务注册中心为例,HSF 使用的是基于数据库的ConfigServer,Dubbo默认使用的是ZooKeeper。

服务注册中心负责服务的发布和通知,通常支持对等集群部署,某一个服务注册中心宕机并不会导致整个服务注册中心集群不可用。即便整个服务注册中心全部宕机,也只影响新服务的注册和发布,不影响已经发布的服务的访问。

服务治理中心通常包含服务治理接口和服务治理Portal,架构师、测试人员和系统运维人员通过服务治理Portal对服务的运行状态、历史数据、健康度和调用关系等进行中视化的分析和维护,目标就是要持续优化服务,防止服务架构腐化,保证服务高质量运行。

功能特性

分布式服务框架的功能特性

  • 服务订阅发布
  • 服务路由
  • 集群容错
  • 服务调用
  • 多协议
  • 序列化方式
  • 统一配置

性能特性

  • 高性能
  • 低时延
  • 性能线性增长

可靠性

服务治理

  • 服务运行态管控
  • 服务监控
  • 服务生命周期管理
  • 故障快速定界定位
  • 服务安全

通信框架

  • RPC一般使用长连接
  • 一般使用NIO,采用多路复用技术,一个多路复用器Selector可以同时轮询多个Channel。由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制。这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。
  • 常用的NIO框架:Netty

功能设计

分布式服务框架的底层通信框架首先是一个通用的通信框架,它不应该与具体的协议绑定。基于通信框架之上,可以构建私有协议栈和公有协议栈。

服务端设计

  1. 提供上层API(屏蔽底层NIO框架),用于初始化服务端实例,设置服务端通信相关参数,包括服务端的I/O线程池(线程组参数)、监听地址、TCP相关参数、接收和发送缓冲区大小等;
  2. 提供可扩展的编解码插件,用户可以通过扩展的方式自定义协议的编码和解码;
  3. 提供拦截面,用于私有协议栈开发。

服务器端最重要的设计原则有三条:
1. 服务端只提供上层的API,不与任何具体协议绑定;
2. 服务端提供给用户的API要尽量屏蔽底层的通信细节,防止底层的变更引起上层级联变更。
3. 服务端功能上不要求全,重点在可扩展性上。

基于Netty开发通信框架服务端,有以下几个关键点需要掌握。
1. 用于接收客户端连接的线程池:通常被称为bossGroup,它的构造方法与处理I/O读写的线程池相同(workerGroup),都是通过new NioEventLoopGroup创建。bossGroup的线程数建议设置为1,因为它仅负责接收客户端的连接,不做复杂的逻辑处理,为了尽可能少地占用资源,它的取值越小越好。
2. TCP参数设置:建议 API层面开发TCP参数设置,为一些特殊的私有协议预留扩展点,对于大多数的开发者,使用默认值即可。Netty的ChannelOption类提供了对TCP参数的封装。由于是工具常量类,未来发生变更的可能性很小,因此可以直接开放给上层使用。
3. 编解码框架的定制:通信框架封装Netty的MessageToByteEncoder和LengthFieldBasedFrameDecoder,提供统一、通用的编解码接口或者抽象类,由用户实现编解码接口,实现编解码的灵活定制。
4. 通信层业务逻辑的定制:利用Netty的ChannelPipeline,将ChannelHandler的链式编排能力以参数或者配置化的方式工发出来,由用户灵活扩展,实现协议层逻辑定制。例如示例代码中的超时检测、握手认证等。需要指出的是,如果担心底层通信框架API发生变更直接影响用户的代码,可以在Netty的ChannelHandler上面再抽象包装一层,通过Facade模式屏蔽底层的实现细节。

客户端设计

基于Netty Client的客户端创建流程:
1. 创建Bootstrap实例,Bootstrap是Socket客户端创建工具类,用户通过Bootstrap可以方便地创建Netty的客户端并发起异步TCP连接操作
2. 初始化TCP连接参数,设置编解码Handler和其它业务Handler
3. 调用Bootstrap的connect方法异步发起连接
4. 采用设置连接监听器的方式,用于连接结果异步通知
5. 服务端返回TCP握手应答,系统回调监听器操作完成接口
6. 在操作完成接口中实现相关业务逻辑,通知客户端连接操作完成

可靠性设计

链路有效性检测

心跳检测机制,有三个层面
TCP层面
协议层的心跳检测
应用层的心跳检测
不同的协议,心跳检测机制也存在差异,归纳起来主要分为两类:
- Ping-Pong
- Ping-Ping
无论发生心跳超时还是心跳失败,都需要关闭链路,由客户端发起重连操作,保证链路能够恢复正常。
Netty的心跳检测实际上利用了链路空闲检测机制实现的,它的空闲检测机制分为三种:
- 读空闲
- 写空闲
- 读写空闲
Nety的默认读写空闲机制是发生超时异常,关闭连接。但是可以定制它的超时实现机制,以使支持不同的用户场景。

断连重连机制
消息缓存重发
资源优雅释放

通过注册JDK的ShutdownHook来实现,当系统接收到退出指令后,首先标记系统处于退出状态,不再接收新的消息,然后将积压的消息处理完,最后调用资源回收接口将资源销毁,最后各线程退出执行。
通常优雅退出有个时间限制,如30s,如果到时间仍未退出,则由监控脚本kill -9 pid,强制退出。
Netty提供了完善的优雅停机接口,通过调用相关接口,可以实现线程池、消息队列、Socket句柄、多路复用器等的资源释放。

性能设计

性能差的三个可能原因:
- 网络传输方式问题
- 序列化性能差
- 线程模型问题
影响通信性能三方面
- 传输
- 协议
- 线程

高性能之道

  • 异步非阻塞通信
  • 高效的I/O线程模型
  • 高性能的序列化框架

最佳实践

误区1:不指定线程池线程大小

EventLoopGroup bossGroup=new NioEventLoopGroup(线程池大小);

如果不指定线程池大小,Netty会优先使用-Dio.netty.eventLoopThreads指定的值,如果用户没有设置,默认采用CPU Core * 2。如果负载非常轻,可以设置为1即可。
建议配置值1<=N<=CPU Core。如果I/O逻辑复杂,建议根据实际性能测试结果逐步调大线程数,尽量不要使用系统默认值。

误区2:I/O线程池使用不当,导致通信线程膨胀


举报

相关推荐

0 条评论