《Netty 进阶之路》、《分布式服务框架原理与实践》作者李林锋深入剖析Netty消息接收类故障案例。李林锋此后还将在 InfoQ 上开设 Netty 专题持续出稿,感兴趣的同学可以持续关注。
1. 背景
1.1 消息接收类故障
尽管Netty应用广泛,非常成熟,但是由于对Netty底层机制不太了解,用户在实际使用中还是会经常遇到各种问题,大部分问题都是业务使用不当导致的。Netty使用者需要学习Netty的故障定位技巧,以便出了问题能够独立、快速的解决。
在各种故障中,Netty服务端接收不到客户端消息是一种比较常见的异常,大部分场景下都是用户使用不当导致的,下面我们对常见的消息接收接类故障进行分析和总结。
1.2 消息接收类故障定位技巧
如果业务的ChannelHandler接收不到消息,可能的原因如下:
业务的解码ChannelHandler存在BUG,导致消息解码失败,没有投递到后端。
业务发送的是畸形或者错误码流(例如长度错误),导致业务解码ChannelHandler无法正确解码出业务消息。
业务ChannelHandler执行了一些耗时或者阻塞操作,导致Netty的NioEventLoop被挂住,无法读取消息。
执行业务ChannelHandler的线程池队列积压,导致新接收的消息在排队,没有得到及时处理。
对方确实没有发送消息。
定位策略如下:
在业务的首个ChannelHandler的channelRead方法中打断点调试,看是否读取到消息。
在ChannelHandler中添加LoggingHandler,打印接口日志。
查看NioEventLoop线程状态,看是否发生了阻塞。
通过tcpdump抓包看消息是否发送成功。
2. 服务端接收不到车载终端消息
2.1 业务场景
车联网服务端使用Netty构建,接收车载终端的请求消息,然后下发给后端其它系统,最后返回应答给车载终端。系统运行一段时间后发现服务端接收不到车载终端消息,导致业务中断,需要尽快定位出问题原因。
2.2 故障现象
服务端运行一段时间之后,发现无法接收到车载终端的消息,相关日志示例如下:
从日志看,服务端每隔一段时间(示例中是15秒,实际业务时间是随机的)就会接收不到消息,隔一段时间之后恢复,然后又没消息,周而复始。跟车载终端确认,终端设备每隔固定周期就会发送消息给服务端(日志分析),因此排除是终端没发消息导致的问题。怀疑是不是服务端负载过重,抢占不到CPU资源导致的周期性阻塞,采集CPU使用率,发现CPU资源不是瓶颈,排除CPU占用率高问题。
排除CPU之后,怀疑是不是内存有问题,导致频繁GC引起业务线程暂停。采集GC统计数据,示例如下:
通过CPU和内存资源占用监控分析,发现硬件资源不是瓶颈,问题应该出在服务端代码侧。
2.3 故障分析
从现象上看,服务端接收不到消息,排除GC、网络等问题之后,很有可能是Netty的NioEventLoop线程阻塞,导致TCP缓冲区的数据没有及时读取,故障期间采集服务端的线程堆栈进行分析,示例如下:
从线程堆栈分析,Netty的NioEventLoop读取到消息后,调用业务线程池执行业务逻辑时,发生了RejectedExecutionException异常,由于后续业务逻辑由NioEventLo