chapter23 可靠性
1、可靠性需求
- 宕机的代价
- Netty可靠性需求
- Netty主要应用场景
- RPC框架
- 私有协议的基础通信框架
- 公有协议的基础通信框架
- Netty的运行环境
- 会面临恶劣的网络环境
- Netty主要应用场景
2、Netty高可靠性设计
- 网络通信类故障
- 客户端连接超时
- NIO类库并没有现成的连接超时接口供用户直接使用,需要用户自己封装实现,Netty通过创建ScheduledFuture挂载在Reactor线程上,用于定时监测是否发生连接超时。
- 通信对端强制关闭连接
- Netty自动对通信对端强制关闭连接进行了资源的释放。
- 链路关闭
- 如果对方合法关闭TCP连接,并不会发生异常,Netty自动对该类进行了处理,通过判断Channel read操作的返回值进行不同的逻辑处理,-1说明链路关闭,Netty也会关闭链路。
- 定制IO故障
- 客户端的断连重发机制
- 消息的缓存重发
- 接口日志中详细记录故障细节
- 运维相关功能
- Netty的处理策略是发生IO异常,底层的资源由他负责释放,同时将异常堆栈的信息以事件的形式通知给上层用户。
- 客户端连接超时
链路的有效性检测
心跳检测三个层面:
- TCP层面的心跳检测,即TCP的Keep-Alive机制,它的作用域是整个TCP协议栈
- 协议层的心跳检测,主要存在于长链接协议中,如SMPP协议
- 应用层心跳检测,主要由各业务产品通过约定方式定时给对方发送心跳消息实现。
不同的协议,心跳检测机制主要归两类
- Ping-Pong型心跳:一方发送ping,一方回复pong,属于请求-响应心跳
- Ping-Ping型心跳:不区分请求和应答,双方按照约定定时发送ping消息,属于双向心跳
心跳检测策略:
- 连续N次心跳检测都没有收到对方的Pong应答消息或者ping消息,则认为链路已经逻辑失效,即心跳超时
- 读取和发送心跳消息的时候如果直接发生IO异常,说明链路已经失效,即心跳失败。
- 无论心跳超时、失败都需要关闭链路,发起重连操作。
Netty提供的三种空闲检测机制:
- 都为链路持续时间t没有收到相应的消息,netty默认的读写空闲机制是发生超时异常,关闭连接。用户可以定制实现不同业务需求。
- 读空闲
- 写空闲
- 读写空闲
- Reactor线程的保护
- 异常处理要谨慎
- 一些特殊场景下会发生非IO异常,因此仅仅捕获IO异常可能会导致Reactor线程跑飞。为了防止一定要在循环体内捕获Throwable,而不是IO异常或者Exception。
- 捕获Throwable之后,让休眠1S,防止死循环导致的异常环绕,然后继续执行恢复,这样处理的核心理念是:
- 某个消息的异常不应该导致整条链路不可用
- 某条链路不可用不应该导致其他链路不可用
- 某个进程不可用不应该导致其他集群点不可用
- 规避NIO BUG
- 如epoll bug
- 异常处理要谨慎
内存保护
- NIO通信的内存保护主要集中在如下:
- 链路总数的控制:每条链路都包含发送和接收缓冲区,链路个数太多容易导致内存溢出。
- 单个缓冲区的上限控制:防止非法长度或者消息过大导致内存溢出
- 缓冲区内存释放:防止因为缓冲区使用不当导致内存溢出。
NIO消息发送队列的长度上限控制
- 缓冲区的内存泄漏保护
- Netty提供了内存池和对象池,需要自己管理内存的申请和释放;为了防止因为用户遗漏导致内存溢出,Netty在Pipeline的尾Handler中自动对内存进行了释放,TailHandler。
- 缓冲区溢出保护
- 缓冲区两种创建方式:
- 容量预分配,在实际读写过程中如果不够再扩展
- 根据协议消息长度创建缓冲区
- 缓冲区的内存泄漏保护
流量整形
- 定义:是一种主动调整流量输出速率的措施。
- Netty流量整形的两个作用:
- 防止由于上下游网元性能不均衡导致下游网元被压垮,业务流程中断。
防止由于通信模块接收消息过快,后端业务线程处理不及时导致的“撑死”问题
- 全局流量整形
- 作用范围:进程级,无论创建多少个Channel,它的作用域针对所有的Channel
- 用户可以通过参数设置:报文的接收速率、报文的发送速率、整形周期。
- Netty流量整形的原理:
- 对每次读取到的ByteBuf可写字节数进行计算,获取当前的报文流量,然后与流量整形阀值对比。如果达到阀值,则计算等待时间delay,将当前的ByteBuf放到定时任务Task中缓存,由定时任务线程池在延迟delay之后继续处理该ByteBuf。
- 如果达到整形阀值,则对新接收的ByteBuf进行缓存,放入线程池的消息队列中,稍后处理。
- 定时任务的延时时间根据检测周期T和流量整形阀值计算得来。
- 如果流量整形的阀值limit越大,流量整形的精确度越高,流量整形功能是可靠性的一种保障,他无法保证100%的精确。
- 流量整形和流控的的最大区别在于流控会拒绝消息,流量整形不会拒绝和丢弃消息。
- 链路级流量整形
- Netty即支持全局流量整形也支持链路的流量整形。
- 单链路流量整形与全局流量整形的最大区别就是它以单个链路为作用域,可以对不同的链路设置不同的整形策略。
- Netty也支持用户自定义流量整形策略
- 全局流量整形
优雅停机接口
3、优化建议
- 发送队列容量上限控制
- Netty的NIO消息发送队列ChannelOutboundBuffer并没有容量上限控制,它会随着消息的积压自动扩展,直到达到0x7fffffff
- 如果网络对方处理速度比较慢,导致TCP滑窗长时间为0;或者消息发送方发送速度过快,或者一次批量发送消息过大,都可能会导致ChannelOutboundBuffer的内存膨胀,可能会导致内存溢出。
- 建议:
- 在启动客户端或者服务端的时候,通过启动项的ChannelOption设置发送队列长度,或者通过-D启动参数配置该长度。
- 回推发送失败的消息
- Mina提供了简单的实现,但Netty并没有,只是简单的销毁
- 策略:
- 缓存重发策略:当链路发生异常之后,尚未发送成功的消息自动缓存,待链路恢复正常之后重发失败的消息
- 失败删除策略:当链路发生异常之后,尚未发送成功的消息自动销毁,它可能是非重要消息,如日志消息,也可能是由业务直接监听异常并做特殊处理