Netty实现长连接服务端跟客户端,使用单独的业务线程池,并支持心跳
背景
前阵子完成过一个系统,对接某交易所接口,通过长连接收发交易报文,并由应用程序发送心跳维持长连接。受限于开发平台的限制,只能采用传统的BIO实现。好在交易量并不大,未出现性能问题,一直稳定运行。但BIO始终是老掉牙的东西,后来做为业余的练习,通过NIO实现了底层的通讯框架。鉴于NIO的epoll bug,这次试试通过Netty来实现,包括服务端代码和客户端代码,服务端为后台程序,客户端有Swing UI。
程序实现的功能
服务端,等待客户端连接,处理客户端的请求;将请求消息加上部分内容后返回给客户端;
客户端,连接并发送请求,接收服务端的响应;
服务端跟客户端均发送心跳,ping-ping模式;
关于Netty的一些事
在开始之前,需要说说有关Netty的一些内容。
Netty是采用的Reactor主从多线程模型(这个可能有分歧,也有人认为是Reactor多线程模型);
通常bossGroup只需要设置成1个线程即可;当需要监听多个端口,并采用同一个ServerBootstrap启动时,才有必要设置成多线程,每个端口会分派给一个固定线程进行监听;这种情况很少见。
workerGroup线程数默认为CPU数*2;
默认情况下,所有ChildHandler均由workerGroup执行,如果其中有耗时操作,则会阻塞workerGroup线程,导致不能及时处理其他channel的IO读写事件;所以一般需要单独的业务线程池,来处理具体的业务逻辑;添加Handler时,可以通过参数指定执行Handler的线程池;
实现
实现起来其实简单。Pipeline中包括LoggingHandler(通过日志观察Netty执行过程)、Decoder、NettyBusinessDuplexHandler(业务处理入口,运行在业务线程池)、NettyHeartBeatDuplexHandler(心跳处理,包括与之对应IdleStateHandler),如下图所示:
心跳机制为Ping-ping模式,即双方都发送心跳,但无需响应心跳。20秒内未发送任何内容则发送心跳,60秒未收到任何内容则认为超时,关闭连接。略作修改也可以支持Ping-pong模式,即客户端发起心跳,服务端将心跳原路返回。
报文为头部+消息体格式,头部为17个字节,依次为4个int字段+1个byte字段。4个int字段依次为magicNumber(魔幻数,此值不对的报文为非法输入,直接丢弃),length(后面消息体长度),messageType(消息类型,1为业务消息,2为心跳),logId(请求方生成随机整数值,响应方原路返回)ÿ