1.问题简单描述:netty作为客户端连接网元设备,channel1接收数据,然后通过channel2写给上层系统,但是有时候上层接收的数据会部分丢失。
2.首先先看两个知识点:
(1)Nagle算法
TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据(一个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据),从而避免避免网络中充斥着许多小数据块,以此提高网络带宽利用率。
(2)TCP的延迟ACK机制
相对于UDP,TCP是可靠的,每一个数据包的正确发送都会受到一个确认消息的过程,可以简单的把它视一个发送,一个反馈。但无论发送还是反馈都是有成本的,所以就有了延迟ACK机制。
TCP是传输层协议的,它是基于ip层协议的数据帧的。即使一次发送一个字节的数据,也需要一个几十字节的IP包头来装配,更何况TCP的传输是两步的,是需要反馈确认的,那样的话效率就非常低。
所以就像我们合并HTTP请求一样,TCP自身也有一种机制来合并一些ACK反馈消息,这就是延迟ACK机制,延迟ACK机制就是让接收方在收到数据后不立即反馈ACK消息,而是等到一小段时间(这个没有细研究过具体多长时间),如果还有收到其他包就把这些ACK消息一起放入一个包中反馈给客户端,这样ack消息发送次数就少了。
3.解决办法
所以当网元返回的数据是大量小数据包,系统在收到这些数据包后向上层写数据的时候会先缓存,netty有自己的缓存队列WriteRequestQueue,而当队列长度大于设置的最小值时,就无法写入,导致写入出问题。所以一方面可以设置这个最小值,一方面在写数据的时候判断channel的isWritable属性,如果为true则写数据,为false则先缓存本次数据,和下次数据一并再写入,然后就是禁用Nagle算法。
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("writeBufferLowWaterMark",64 * 1024);//默认是32k
bootstrap.setOption("writeBufferHighWaterMark",128 * 1024);x
写数据时候先判断channel.isWritable(),如果不可写入,等待一段时间后再写入。