转载: http://mp.weixin.qq.com/s?__biz=MzAwNzY4OTgyNA==&mid=2651824719&idx=1&sn=c854f9fb5d6a85e9dbac177e08f95ca9&scene=1&srcid=0827jY7OYr9z2q8SgfGyJUAi#rdjava
最近在使用netty做为http客户端经过pool链接tomcat的时候,出现了不少Connection reset by peer 的IOException的异常。便对问题的根源作了细致的调研。redis
通常链接主要分为长链接,短链接和http的keepalive链接。数据库
1.1 长链接:创建完链接后,该链接再也不进行释放。django
优势:性能较高,不须要重复创建tcp链接或者关闭tcp链接bootstrap
基本上不会出现CLOSE_WAIT和TIME_WAIT的问题tomcat
缺点 : 通常须要一个链接池来维护长链接(通常有数据库链接池,http的链接池等) 复杂度较高服务器
1.2 短链接:每次请求均须要tcp三次握手创建链接,业务执行,tcp四次挥手关闭链接。微信
优势:实现简单。
缺点:性能较差。 大部分都是tcp层面上的交互(新建和关闭tcp链接)
系统会出现大量的tcp的状态是:TIME_WAIT 若是没有设置SO_RESUSEADDR ,很容易出现端口被占满的状况。(在关闭完链接时,tcp状态是TIME_WAIT,只有等2个MSL后,才会进行close掉)
1.3 http的keepalive:用于http协议。在http 1.1中,为了解决长链接提出的。
优势:用于维护长链接,提高性能
缺点: 须要在header中进行控制,须要交互控制,相对复杂。
提到keepalive, 容易对下面三种机制混淆:keepalived,tcp的keepalive,http的keepalive
用途:高可用,通常是和lvs一块儿使用。具体可参考:http://outofmemory.cn/wiki/keepalived-configuration
用途:socket链接的保活。在新建socket的时候,能够设置SO_KEEPALIVE 进行打开。
keepalive主要有三个参数:
tcp_keepalive_time: 一个链接须要TCP开始发送keepalive探测数据包以前的空闲时间。以秒为单位
tcp_keepalive_probes: 发送TCP keepalive探测数据包的最大数量,默认是9.若是发送9个keepalive探测包后对端仍然 没有响应,便发送RST关闭掉链接。
tcp_keepalive_intvl: 发送两个TCP keepalive探测数据包的间隔时间,默认是75秒
用途:http的长链接,在http 1.0中使用的为短链接:每一次请求均须要新建tcp链接,http协议数据的发送接收,关闭tcp链接。 该种机制性能很 低,在http 1.1协议中引入了keepalive机制来保持tcp链接。
在http1.0中,所有是短链接,若是想创建长链接,须要在header里面加上keepalive,这样web服务器看到这个字段,不会立马关闭链接。而是将tcp链接维持一段时间。若是须要关闭,则在header中写 keepalive close,来告诉 客户端须要关闭该链接。
在http1.1中,默认会实现keepalive,若是使用的是http1.1协议,header是不须要加上keepalive的。
tomcat8中,若是发送的是http1.0的协议。 tomcat8返回的均是1.1的协议。而且无论请求的header有没有Connection:keepalive ,均会在返回的header中加上connection:close 。下面是访问tomcat8的截图:
GET请求是http 1.0,可是返回的是1.1的协议:
返回的header里面有Connection:close
tomcat8主要有两个参数来控制keepalive的机制。keepAliveTimeout 和maxKeepAliveRequests
keepAliveTimeout: 默认和soTimeout 值保持一致,该值为20000ms,也就是在这么长时间内没有通讯,tomcat会关闭掉该链接。设置为-1 则表明不会关闭该链接。
maxKeepAliveRequests :默认为100,也就是在keepAliveTimeout时间内,若是使用次数超过100,则会关闭掉该链接。设置为-1,则表明不会关闭链接。在关闭后,会在返回的header上面加上Connection:close 。
若是须要tomcat保持长链接:可配置 maxKeepAliveRequests = "-1" keepAliveTimeout=-"-1" ,则tomcat8不会关闭掉该链接。
主要须要处理两个地方:
1:maxKeepAliveRequests 链接达到默认的设置次数。则会在header上面加Connection:close。
在接收web服务器返回的数据时,须要检查一下header里面是否有Connection:close,若是close,则须要将该链接从链接池里物理关闭掉。不然容易出现connection reset by peer的异常。
2:keepAliveTimeout 超过该时间没有流量,则会关闭掉链接。
tomcat在链接空闲超过该时间后,会主动关闭掉链接。会向客户端发送FIN命令。
若是是IO(同步socket):则在获取链接的时候须要检查一下该socket的链接状态。 由于tcp在底层已经关闭了该链接。 若是不检查的话,则会SocketCloseException的错误。
若是是NIO(异步channel) :则在selector的时候,read数据的时候,会返回-1,而后将该链接从链接池给物理关闭掉。
Connection reset by peer异常
异常场景:
1: 当咱们往一个对端已经close的通道写数据的时候,对方的tcp会收到这个报文,而且反馈一个reset报文,当收到reset报文的时候,继续作select读数据的时候就会抛出Connect reset by peer的异常。该异常为jdk抛出的异常。在native代码里面抛出。
2:尝试和未开放的服务器端口创建tcp链接时,服务器tcp将会直接向客户端发送reset报文
3:ack报文丢失,而且超出必定的重传次数或时间后,会主动向对端发送reset报文释放该TCP链接
链接池出现该异常分析:
1:因为客户端在收到Connection:close的header时候并无物理关闭该链接,而是将该链接返回到了链接池中。
2:下一个请求拿到该链接发送数据,因为tomcat的该socket通道已经关闭,tomcat接收到该链接时,便会回复一个RST。
3:客户端在读取数据(RST的时候,内部会调用(JDK)SocketChannel.read的时候抛出 java.io.IOException(Connection reset by peer)