Netty 是一个基于NIO的客户、服务器端编程框架, 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
粘包/拆包问题的解决
TCP是一个流的协议,一个完成的包可能会被TCP拆分成多个包进行发送,也可能把多个小包封装成一个大的数据报发送(半包读写问题)。
解决方案:
-
消息定长, 报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。
-
包尾添加特殊分割符, 例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。
-
将消息分为消息头和消息体,消息头中包含标识消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用int32来标识消息的总长度。
Netty使用自定义的编码器(...Encoder)和解码器(...Decoder)来处理消息。只需要将半包解码器的Handler添加到ChannelPipeline中即可。
序列化
另外,当进行远程跨进程服务调用时,需要把被传输的Java对象编码为字节数组或者ByteBuffer对象,而当远程服务读取到ByteBuffer对象或者字节数组时,需要将其编码为发送时的Java对象,这称之为Java编码技术。Java序列化是Java编码技术的一种,不使用的原因是Java序列化无法跨语言, Java序列化后的码流太大,不管是网络传输还是持久化到磁盘。都会导致额外的资源占用,序列化性能差(CPU资源占用高)。主流的有Google的Protobuf、Facebook的Thrift,JBoss Marshlling。
私有协议栈
协议栈功能描述:
-
基于Netty的NIO通信框架,提供高性能的异步通信能力;
-
提供消息的编解码框架,实现POJO的序列化和反序列化
-
提供基于IP地址的白名单接入认证机制;
-
链路的有效性校验机制;
-
链路的断线重连机制;
消息头设计:
可靠性设计:
心跳机制:
Ping-Pong 双向心跳机制。
网络空闲时间达到T,客户端发送Ping,过T时间,客户端没有收到Pong,心跳失败计数+1,收到Pong或者业务消息则失败计数清零,连续N次没有收到Pong消息则关闭链路。
服务端空闲T时间,心跳失败计数+1,收到Ping或者其他消息,技术清零,失败次数达到N,关系链路。
关闭链路后,客户端需要发起重连,服务端需要清空缓存的半包信息,等待客户端重连。
重连机制
如果链路中断,等待INTERVAL时间后,由客户端发起重连操作,如果重连失败,间隔周期INTERVAL之后再继续重连。
无论什么场景下的重连失败,客户端必须保证自身资源被成功及时释放。
重连失败,需要记录异常堆栈信息,方便问题定位。。
重复登录保护
客户端握手成功之后,链路处于正常状态下,不允许客户端重复登录,以防止客户端在异常状态下反复重连导致句柄资源被耗尽。
server在接收到握手消息后,首先进行ip合法性校验,如果成功,则在缓存的地址表中查看客户端是否已经登录,如果已经登录,则拒绝重复登录,返回错误码-1,同时关闭链路,并且在服务端日志中打印错误信息。
为了防止由服务端和客户端对链路状态理解不一致的问题,当服务端连续N次心跳超时之后需要主动关闭链路,同时清空该客户端的缓存信息,保证后续的客户端可以重连。
消息缓存重发
无论是客户端还是服务端,在发生链路中断之后,恢复链路之前,缓存在消息队列的待发送的消息不能丢失。同时考虑到内存溢出风险,应该在消息缓存队列中设置上限。
欢迎关注公众号,让我们一起学习探讨问题