短连接时出现connection reset问题的原因
网上摘取的感觉有用的文章,保存下来,让大家学习交流!
在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家。例如我们线上的网关日志就会抛该错误:

从日志中可以看到是Socket套接字在read数据时抛出了该错误。
导致“Connection reset”的原因是服务器端因为某种原因关闭了Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示“java.net.SocketException: Connection reset”。
可能有同学对复位标志“RST”还不太了解,这里简单解释一下:
TCP建立连接时需要三次握手,在释放连接需要四次挥手;例如三次握手的过程如下:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手:服务器收到syn包,并会确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
可以看到握手时会在客户端和服务器之间传递一些TCP头信息,比如ACK标志、SYN标志以及挥手时的FIN标志等。
除了以上这些常见的标志头信息,还有另外一些标志头信息,比如推标志PSH、复位标志RST等。其中复位标志RST的作用就是“复位相应的TCP连接”。
TCP连接和释放时还有许多细节,比如半连接状态、半关闭状态等。详情请参考这方面的巨著《TCP/IP详解》和《UNIX网络编程》。
前面说到出现“Connection reset”的原因是服务器关闭了Connection[调用了Socket.close()方法]。大家可能有疑问了:服务器关闭了Connection为什么会返回“RST”而不是返回“FIN”标志。原因在于Socket.close()方法的语义和TCP的“FIN”标志语义不一样:发送TCP的“FIN”标志表示我不再发送数据了,而Socket.close()表示我不在发送也不接受数据了。问题就出在“我不接受数据” 上,如果此时客户端还往服务器发送数据,服务器内核接收到数据,但是发现此时Socket已经close了,则会返回“RST”标志给客户端。当然,此时客户端就会提示:“Connection reset”。详细说明可以参考oracle的有关文档:http://docs.oracle.com/javase/1.5.0/docs/guide/net/articles/connection_release.html。
另一个可能导致的“Connection reset”的原因是服务器设置了Socket.setLinger (true, 0)。但我检查过线上的tomcat配置,是没有使用该设置的,而且线上的服务器都使用了nginx进行反向代理,所以并不是该原因导致的。关于该原因上面的oracle文档也谈到了并给出了解释。
此外啰嗦一下,另外还有一种比较常见的错误“Connection reset by peer”,该错误和“Connection reset”是有区别的:
服务器返回了“RST”时,如果此时客户端正在从Socket套接字的输出流中读数据则会提示Connection reset”;
服务器返回了“RST”时,如果此时客户端正在往Socket套接字的输入流中写数据则会提示“Connection reset by peer”。
“Connection reset by peer”如下图所示:

前面谈到了导致“Connection reset”的原因,而具体的解决方案有如下几种:
出错了重试;
客户端和服务器统一使用TCP长连接;
客户端和服务器统一使用TCP短连接。
首先是出错了重试:这种方案可以简单防止“Connection reset”错误,然后如果服务不是“幂等”的则不能使用该方法;比如提交订单操作就不是幂等的,如果使用重试则可能造成重复提单。
然后是客户端和服务器统一使用TCP长连接:客户端使用TCP长连接很容易配置(直接设置HttpClient就好),而服务器配置长连接就比较麻烦了,就拿tomcat来说,需要设置tomcat的maxKeepAliveRequests、connectionTimeout等参数。另外如果使用了nginx进行反向代理或负载均衡,此时也需要配置nginx以支持长连接(nginx默认是对客户端使用长连接,对服务器使用短连接)。
使用长连接可以避免每次建立TCP连接的三次握手而节约一定的时间,但是我这边由于是内网,客户端和服务器的3次握手很快,大约只需1ms。ping一下大约0.93ms(一次往返);三次握手也是一次往返(第三次握手不用返回)。根据80/20原理,1ms可以忽略不计;又考虑到长连接的扩展性不如短连接好、修改nginx和tomcat的配置代价很大(所有后台服务都需要修改);所以这里并没有使用长连接。ping服务器的时间如下图:

最后的解决方案是客户端和服务器统一使用TCP短连接:我这边正是这么干的,而使用短连接既不用改nginx配置,也不用改tomcat配置,只需在使用HttpClient时使用http1.0协议并增加http请求的header信息(Connection: Close),源码如下:
|
1
2
|
httpGet.setProtocolVersion(HttpVersion.HTTP_1_0);httpGet.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE); |
最后再补充几句,虽然对于每次请求TCP长连接只能节约大约1ms的时间,但是具体是使用长连接还是短连接还是要衡量下,比如你的服务每天的pv是1亿,那么使用长连接节约的总时间为:
|
1
|
1亿*1ms=10^8*1ms=10^5*1s=10^5*1h/3600≈27.78h |
神奇的是,亿万级pv的服务使用长连接一天内节约的总时间为27.78小时(竟然大于一天)。
所以使用长连接还是短连接大家需要根据自己的服务访问量、扩展性等因素衡量下。但是一定要注意:服务器和客户端的连接一定要保持一致,要么都是长连接,要么都是短连接。
短连接时出现connection reset问题的原因的更多相关文章
- Oracle JDBC 连接卡死后 Connection Reset
坑 这绝对是我碰计算机以来遇到的第一大坑! 症状: 在Linux主机上远程登录,执行一个简单的Oracle的JDBC连接程序(jar包),结果硬生生的卡在了连接建立验证阶段,然后等上几分钟后因为连接超 ...
- 解决ssh_exchange_identification:read connection reset by peer 原因
服务器改了密码,试过密码多次后出现: ssh_exchange_identification: read: Connection reset by peer 可以通过ssh -v查看连接时详情 Ope ...
- Redis连接出现Error: Connection reset by peer的问题是由于使用Redis的安全模式
现在网上一查出现安全模式的连接,基本都是要关闭服务端的操作,其实这种方式是不正确的,最有效的解决方式是使用stunnel进行安全模式的连接. 我碰到的问题是微软云(其实我不想用!)连接Redis,默认 ...
- Oracle JDBC 连接卡死后 Connection Reset解决过程
https://www.cnblogs.com/pthwang/p/8949445.html
- 执行git pull时提示Connection reset by 13.229.188.59 port 22
问题如下图: 解决办法: 1. 2. 3. 4. 5. 6.
- tcp连接时,BROKEN PIPE错误的原因以及解决方法
问题: 写了一个server和一个client,UNIX套接字的,server不断接收消息并打印出来,client是一个交互程序,输入一个消息回车发送,接着又可以输入消息.出问题了:当server监听 ...
- 一次SocketException:Connection reset 异常排查
问题描述 上一期的需求上线之后,线上多了一个异常:Connection reset.如下: [2017-03-22 00:45:00 ERROR] [creativeAuditTaskSchedule ...
- connection reset 分析解决(转载)
文章转自:https://my.oschina.net/xionghui/blog/508758;记录下来以便以后复习查阅; 在使用HttpClient调用后台resetful服务时,“Connect ...
- Connection reset原因分析和解决方案
在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家.例如我们线上的 ...
随机推荐
- python 之前函数补充(__del__, item系列, __hash__, __eq__) , 以及模块初体验
__str__ : str(obj) , 需求必须实现了 __str__, 要求这个方法的返回值必须是字符串 str 类型 __repr__ (意为原型输出): 是 __str__ 的备胎( ...
- 【bzoj2226】[Spoj 5971] LCMSum 欧拉函数
题目描述 Given n, calculate the sum LCM(1,n) + LCM(2,n) + .. + LCM(n,n), where LCM(i,n) denotes the Leas ...
- This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its 错误解决办法
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary log ...
- 微信小程序报“app.json”错误解决办法
1.亲测 “app.json未找到入口 app.json 文件,或者文件读取失败,请检查后重新编译.” 是由于新创建的界面xxx.json所在的文件夹为0KB造成的,你可以试着在xxx.json文件内 ...
- Difference Between ZIP and GZIP
From: http://www.differencebetween.net/technology/difference-between-zip-and-gzip/ Summary: 1. GZIP ...
- Android N API预览
Android N for Developers 重要的开发人员功能 多窗体支持 通知 JIT/AOT 编译 高速的应用安装路径 外出瞌睡模式 后台优化 Data Saver 高速设置图块 API 号 ...
- IE下获取不到Response添加的cookie的解决方法
原博客地址: http://blog.csdn.net/wjdd1/article/details/16341189 在百度上查询了好久也没有查询到结果,于是自己用ie的开发者工具进行跟踪,JSESS ...
- IoC与DI
IoC与DI 首先想说说IoC(Inversion of Control,控制倒转).这是spring的核心,贯穿始终.所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期 ...
- Spark0.9.0机器学习包MLlib-Optimization代码阅读
基于Spark的一个生态产品--MLlib,实现了经典的机器学算法,源码分8个文件夹,classification文件夹下面包含NB.LR.SVM的实现,clustering文件夹下面包 ...
- easy_install和pip的安装及使用
在终端输入命令报错后,在网上找到了这篇博客,用easy_install命令安装pip,问题解决 Fatal error in launcher: Unable to create process us ...