netty主动断开连接_Netty里配置的SO_REUSEADDR参数作用是啥,应该如何调优?

eb36ee1058c12359766a45d75e6e2d2d.png

点击上方蓝字关注我吧!

本篇文章大概1500字,阅读时间大约5分钟

本文总结了SO_REUSEADDR参数的用途以及调优方法,不妨带着如下4个问题阅读:

1、该参数是用来做什么的?

2、客户端和服务端,在什么场景下需要配置该参数?

3、TCP不在TIME-WAIT态下,可以允许端口复用么?

4、如何查看某个端口是否被进程占用?

142e935c3a081e9e058749026d5d1103.gif

众所周知,在TCP四次挥手时,主动关闭方一般是调用close方法触发TCP协议栈发一个【FIN】报文段给对端,此时主动方进入【FIN-WAIT-1】态,当收到对端的【ACK】报文段后,主动关闭方会进入【FIN-WAIT-2】态,当主动关闭方收到对端的【FIN】报文段,并回送【ACK】报文段后,会进入TIME-WAIT态持续等待2MSL才最终CLOSED。针对服务端,假设它重启了,服务端是主动关闭方,按照TCP协议规范,该条连接的TCP状态会在真正断开之前进入TIME-WAIT态(该图画反了):

70bcb2b9fbadeba16ddf3558938d73c5.png

而被动关闭方只有CLOSE-WAIT和LAST-ACK中间态,不会有长达2MSL时间的等待。如果服务端是异常宕机或者在外部被kill命令杀死,然后在立即重启,那么大概率就会出现

Address already in use的异常。可能要等上几分钟才能再次重启成功,出于运维效率和服务本身的健壮性角度考虑,这是无法接受的。

可以使用nc命令(全称为NetCat)模拟该异常,nc非常实用,是一个简单可靠的网络工具,可通过TCP或UDP协议传输数据。同时它还是一个网络应用Debug分析器,可以根据需要创建各种不同类型的网络连接。如下是Java版本demo:

e1c2b42976a213e1430551d64f2820bc.png

启动后打印resue address:false

7da8481bc4fb1070add70d6e3f3dcbbc.png

此时用nc命令连接该服务器,然后在外部杀掉该服务端进程,在立即重启服务器就会报异常——Address already in use,将代码修改为serverSocket.setReuseAddress(true),再次重复上面过程就不会出现上述异常了。注意setReuseAddress必须在bind函数调用之前执行,否则无效。之所以出现这种错误,就是因为主动关闭方需要等待2MSL时间才能彻底释放该链接,处于TIME-WAIT的连接占用的资源不会被操作系统内核释放。而服务器一般都是固定端口,并且按照TCP协议规范——一个链接的四元组必须是唯一的,四元组即源端口,源IP,目的端口和目的IP。四个属性里只要有一个不一样就可以,而操作系统本身的限制更苛刻,故立即重启服务器,操作系统默认会阻止新的监听套接字重复绑定该端口,表现就是抛出“地址已经被使用”的异常。

如下,使用Netty编写服务器时,可以在服务端手动配置该参数为生效:

.option(ChannelOption.SO_REUSEADDR, true)

因为默认情况这个值都为false,在TCP协议中即表现为0,表示关闭。而透传到Java中,其配置的API在不同JVM有不同实现,为了保险起见,编写TCP或者HTTP的服务端程序,一定要主动设置这个参数为true。

下面在看一个问题:当服务端作为主动关闭方,进入TIME-WAIT态之前处于FIN-WAIT-2态,只有当对方的FIN报文段发过来,服务端才转移到TIME-WAIT态,假设由于网络状态不佳,导致客户端发的FIN报文段丢包,此时服务器会一直处于FIN-WAIT-2态,此时配置SO_REUSEADDR还管用么?

答案是管用,即根据TCP协议的规范,关闭TCP连接时不一定是处于TIME-WAIT态才允许复用端口,只是常见的例子都是在TIME-WAIT态翻车的。

以上,就是关于该参数的一些总结和梳理,另外在线上出现该异常后,可以使用netstat命令或者lsof命令查看端口占用情况,比如查看22端口被谁占用。

第一种方法:使用netstat -lptn | grep 22,其中-l是查看处于监听状态的Socket,-t是TCP协议,-p进程名称,-n(number)直接显示IP地址,而不是host:

a0d4916994e6c830f034a323bea10685.png

第二种方法:使用lsof,在linux上一切皆文件,TCP Socket连接也是一个fd。因此使用lsof也可以查看,即lsof -n -P -i:22,写起来不如第一种爽快:

de10e61f6ed96671e6783385800e8329.png

其中-n表示不将IP转换为hostname,-P(大写)表示不将port转换为service name,-i:port表示端口号为22的进程。可以看到22端口被进程号为7150的sshd进程监听。

小结:

1、SO_REUSEADDR参数在服务端和客户端都能使用并生效

2、必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR都打开才能起作用

3、在服务端配置,必须在bind方法之前设置才能生效

4、一般都是客户端主动关闭连接,此时客户端用不到SO_REUSEADDR参数,因为客户端一般使用临时端口,端口重用的概率极低

5、使用netstat -laptn命令结合gerp可以查询端口占用情况

6、TCP协议规定TIME-WAIT会一直持续2MSL(即两倍的TCP报文段最大生存时间),以此来确保旧连接不会对新连接产生影响。而处于TIME-WAIT态的连接资源不会被内核释放,所以作为服务器,尽量不要频繁的主动断开连接,比如可以使用连接池复用技术以减少TIME-WAIT态导致的资源浪费。

END

点亮在看,你最好看

点击此处写留言~

6793636bee6471138a3da35bc8f46aaa.png 1804868bac237cab54defd98277a1862.gif

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值