Jedis分析(二) - wtstengshen/blog-page GitHub Wiki

本文主要分析一下Jedis的Socket的的部分,主要是Jedis的redis.clients.jedis.Connection类中的connect()方法;主要原因是,最近公司上线几台job机器的TCP连接很高,排查了一下TCP的连接的问题,同时在使用Jedis编写redis监控的时候发现了一些其他关于TCP的问题,一并总结一下。 ##1,TCP正常情况,建立连接和断开连接的过程

![tcp三次握手和四次挥手](http://7xkio7.com1.z0.glb.clouddn.com/tcp%E6%AD%A3%E5%B8%B8%E6%83%85%E5%86%B5%E5%BB%BA%E7%AB%8B%E5%92%8C%E6%96%AD%E5%BC%80%E8%BF%9E%E6%8E%A5.png)

正常情况下,tcp的三次握手和四次挥手的过程,这里主要说的是正常关闭连接的时候,主动发起FIN请求,在进入Time_wait状态会停留[2MSL][1]的时间用于重发ACK,所以正常情况下,只要是主动断开连接的一方,都会在Time_wait状态停留一段时间; 我在使用Jedis监控redis服务器是,会每隔一段时间向redis服务器发送ping命令,然后得到响应,最后关闭连接;使用netstat命令查看客户端的tcp状态的时候,并没有发现time_wait状态,很是好奇怎么回事。

##2,使用tcodump抓包获取信息 使用tcpdump抓了一下包,发现了其中的原因:

16:55:43.858018 IP 10.66.0.161.62498 > cacheredis.test18.com.6379: Flags [S], seq 1479484124, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 978468219 ecr 0,sackOK,eol], length 0
16:55:43.932613 IP cacheredis.test18.com.6379 > 10.66.0.161.62498: Flags [S.], seq 3789186145, ack 1479484125, win 14480, options [mss 1460,sackOK,TS val 863510570 ecr 978468219,nop,wscale 7], length 0
16:55:43.932672 IP 10.66.0.161.62498 > cacheredis.test18.com.6379: Flags [.], ack 3789186146, win 4117, options [nop,nop,TS val 978468293 ecr 863510570], length 0
16:55:43.932899 IP 10.66.0.161.62498 > cacheredis.test18.com.6379: Flags [P.], seq 1479484125:1479484139, ack 3789186146, win 4117, options [nop,nop,TS val 978468293 ecr 863510570], length 14
16:55:43.942696 IP cacheredis.test18.com.6379 > 10.66.0.161.62498: Flags [.], ack 1479484139, win 114, options [nop,nop,TS val 863510645 ecr 978468293], length 0
16:55:43.942699 IP cacheredis.test18.com.6379 > 10.66.0.161.62498: Flags [P.], seq 3789186146:3789186153, ack 1479484139, win 114, options [nop,nop,TS val 863510645 ecr 978468293], length 7
16:55:43.942753 IP 10.66.0.161.62498 > cacheredis.test18.com.6379: Flags [.], ack 3789186153, win 4117, options [nop,nop,TS val 978468303 ecr 863510645], length 0
// 最后一个包
16:55:43.942878 IP 10.66.0.161.62498 > cacheredis.test18.com.6379: Flags [R.], seq 1479484139, ack 3789186153, win 4117, length 0
其中,最后一个tcp包,发现Jedis的client发送了一个RST包,正常情况下是应该发送FIN包,最后查看了一下Jedis的connection方法,是一个TCP的SoLinger参数起的作用:
socket.setSoLinger(true, 0); // Control calls close () method,
                            // the underlying socket is closed
查看了一下源码,关于SoLinger选项的解释:
Specify a linger-on-close timeout. This option disables/enables immediate return from a close() of a TCP Socket. Enabling this option with a non-zero Integer timeout means that a close() will block pending the transmission and acknowledgement of all data written to the peer, at which point the socket is closed gracefully. Upon reaching the linger timeout, the socket is closed forcefully, with a TCP RST. Enabling the option with a timeout of zero does a forceful close immediately. If the specified timeout value exceeds 65,535 it will be reduced to 65,535.
如果设置这个参数是setSoLinger(true,0),就会立即发送一个TCP的RST包,使用RST包终止tcp的连接状态; 终止一个连接的正常方式是发送FIN,正常情况没有任何数据丢失。但是也可以发送一个复位报文段释放一个连接,异常释放。发送RST的一方,会丢弃任何未发送的数据,而接收方不会对RST包做ACK响应,收到RST的一方将终止该连接,并通知应用层连接复位。

##3,写在最后 1,Socket在close的时候不正常发送FIN,直接发送RST有什么好处?Jedis这样做的目的是什么? 2,Java程序,接收到RST包,会抛出一个java.net.SocketException: Connection reset的异常,那么redis服务端是如何处理的,需要看一下redis的源码。

第一次再看Jedis的源码的时候,并没有在意SoLinger选项带来的影响,而且以前没有做过TCP Socket编程,正常情况大家估计都很清楚,一些异常情况,有些地方确实没有想到,需要再次申请研究TCP的知识,欢迎拍砖。

[1]:https://en.wikipedia.org/wiki/Maximum_segment_lifetime

⚠️ **GitHub.com Fallback** ⚠️