Jedis分析(三)TcpNoDelay - wtstengshen/blog-page GitHub Wiki
接着上篇分析Jedis的connect()方法里的参数进行分析; ####1,socket.setTcpNoDelay(true)的作用在Jedis的connect()方法中,设置了tcpNoDelay为true,看一下源码中的注解,socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to // ensure timely delivery of data[Nagle's algorith][1],Nagle算法是为了减少网络中小包较多的问题;减少必须发送包的个数来增加网络软件系统的效率; TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。如果网络中充斥这大量的,只有很小数据的tcp包,那么这么多包都需要进行ack,会给网络带来很大的压力,所以为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据,把很多小数据集合在一起发送出去,Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。 Nagle算法的伪代码,摘自维基百科:/** * Disable Nagle's algorithm for this connection. Written data * to the network is not buffered pending acknowledgement of * previously written data. *<P> * Valid for TCP only: SocketImpl. * <P> * @see Socket#setTcpNoDelay * @see Socket#getTcpNoDelay */ public final static int TCP_NODELAY = 0x0001;可以看到他的算法起作用的前提是:有未被确认的数据,同时要发送的数据包的大小小于MSS; 同时,TCP_NODELAY和[TCP_DELAYED_ACKNOWLEDGMENT][2]碰在一起,有可能出现网络延迟40ms的情况;tcp的延迟确认会延迟ack包的发送,等待和数据包一次进行发送。if there is new data to send if the window size >= MSS and available data is >= MSS send complete MSS segment now else if there is unconfirmed data still in the pipe enqueue data in the buffer until an acknowledge is received else send data immediately end if end if end if
##2,代码重现40ms的延迟问题 client和server部署在两个不同的机器上,在client发送小包,查看往返请求的时间;
// client端的java代码
Socket socket = new Socket();
socket.setTcpNoDelay(false);// 这里设置是否禁用TcpNoDelay
socket.connect(new InetSocketAddress("182.168.1.23", 9091), 2000);
String head = "A";
String body = "B\r\n";
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
for (int i = 0; i < 10; i++) {
long startTime = System.currentTimeMillis();
// 分两次发送小包数据
out.write(head.getBytes());
out.write(body.getBytes());
String resLine = reader.readLine();
System.out.println("TTL:"
+ (System.currentTimeMillis() - startTime) + ",resLine = "
+ resLine);
}
in.close();
out.close();
socket.close();
TTL:2,resLine = AB
TTL:0,resLine = AB
TTL:0,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
TTL:0,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
TTL:2,resLine = AB
TTL:41,resLine = AB
TTL:42,resLine = AB
TTL:40,resLine = AB
TTL:42,resLine = AB
TTL:41,resLine = AB
TTL:41,resLine = AB
TTL:43,resLine = AB
TTL:42,resLine = AB
TTL:40,resLine = AB
// 分两次发送小包数据
out.write(head.getBytes());
out.write(body.getBytes());
//改为
out.write((head + body).getBytes());
TTL:3,resLine = AB
TTL:1,resLine = AB
TTL:1,resLine = AB
TTL:0,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
TTL:0,resLine = AB
TTL:1,resLine = AB
##3,最后总结 Jedis设置关闭TcpNoDelay的目的和代码中注释解释的一样,因为40ms的延迟对于请求一次redis来说,实在是太长了,基本上局域网已经是8个来回了,生产上请求一次redis的时间基本上在5ms作用,Jedis这样做也是为了提供响应时间。
[1]:https://en.wikipedia.org/wiki/Nagle%27s_algorithm [2]:https://en.wikipedia.org/wiki/TCP_delayed_acknowledgment