文档章节

Connection reset原因分析和解决方案

xionghuiCoder
 xionghuiCoder
发布于 2015/09/20 16:35
字数 1650
阅读 109044
收藏 40

在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家。例如我们线上的网关日志就会抛该错误:

从日志中可以看到是Socket套接字在read数据时抛出了该错误。


导致“Connection reset”的原因是服务器端因为某种原因关闭了Connection,而客户端依然在读写数据,此时服务器会返回复位标志“RST”,然后此时客户端就会提示“java.net.SocketException: Connection reset”。

可能有同学对复位标志“RST”还不太了解,这里简单解释一下:

TCP建立连接时需要三次握手,在释放连接需要四次挥手;例如三次握手的过程如下:

  1. 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;

  2. 第二次握手:服务器收到syn包,并会确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的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),源码如下:

httpGet.setProtocolVersion(HttpVersion.HTTP_1_0);
httpGet.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);


最后再补充几句,虽然对于每次请求TCP长连接只能节约大约1ms的时间,但是具体是使用长连接还是短连接还是要衡量下,比如你的服务每天的pv是1亿,那么使用长连接节约的总时间为:

1亿*1ms=10^8*1ms=10^5*1s=10^5*1h/3600≈27.78h

神奇的是,亿万级pv的服务使用长连接一天内节约的总时间为27.78小时(竟然大于一天)。

所以使用长连接还是短连接大家需要根据自己的服务访问量、扩展性等因素衡量下。但是一定要注意:服务器和客户端的连接一定要保持一致,要么都是长连接,要么都是短连接。


© 著作权归作者所有

共有 人打赏支持
xionghuiCoder
粉丝 86
博文 34
码字总数 31340
作品 4
海淀
程序员
私信 提问
加载中

评论(9)

黄云堆雪
黄云堆雪
139058
xionghuiCoder
xionghuiCoder

引用来自“eason520”的评论

请问客户端配置长连接 需要怎么写呢, httpClient
对了,补充下:我记得好像httpclient默认就是用的HTTP/1.1;是支持长连接的,只需要配置服务端支持长连接就好。
xionghuiCoder
xionghuiCoder

引用来自“eason520”的评论

请问客户端配置长连接 需要怎么写呢, httpClient
HttpPost httpPost = new HttpPost(uri);
httpPost.addHeader("Connection", "Keep-Alive");
这个是httpclient的代码;然而还需要服务端支持长连接,如果服务端是haproxy或者LVS,也需要配置支持长连接,具体配置参数建议google下。
eason520
eason520
请问客户端配置长连接 需要怎么写呢, httpClient
xionghuiCoder
xionghuiCoder
会用连接池的,但是每次用完会释放连接
luweijava1
luweijava1
httpGet.setProtocolVersion(HttpVersion.HTTP_1_0);
httpGet.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);

设置了这两句,连接池就用不了啦吧。一次连接过后,标记为不可服用,也不会放入连接池中。是这样子吗
漂泊一剑客
漂泊一剑客
楼主讲解的很好,收藏了
xionghuiCoder
xionghuiCoder
26暴露了,忘记打码了
小旭旭87
小旭旭87
我看到了 统一日志 和 自动部署~~
Connection reset

在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家。例如我们线上的网关日志就会...

夜黑人模糊灬
2018/07/20
0
0
ssh连接阿里云错误:ssh_exchange_identification: read: Connection reset by peer

记录一个 ssh 链接阿里云服务器时出现的问题: sshexchangeidentification: read: Connection reset by peer 阿里云的文档给出了指导和解决方案。 问题原因 该问题通常是由于 Linux 系统通过...

angkee
2018/05/31
0
0
java.net.SocketException四大异常解决方案

java.net.SocketException如何才能更好的使用呢?这个就需要我们先要了解有关这个语言的相关问题。希望大家有所帮助。那么我们就来看看有关java.net.SocketException的相关知识。 第1个异常是...

长平狐
2012/08/28
208
0
ssh 错误

机器登录发生sshexchangeidentification: Connection closed by remote host 原因是: /var/empty/sshd 这个文件夹被删 建下就ok #!/bin/bash touch /var/run/utmp chmod 664 /var/run/utmp ......

SibylY
2016/08/09
5
0
Connection reset by peer 的常见原因:

Connection reset by peer的常见原因: 1)服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭; 如果知道实际连接服务器的并发客户数没有超过服务器的承载量,则有可能是中了病...

不忘初心77
2018/06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

idea中导入springboot项目的main方法右键不能run问题

使用idea打开有 main 入口的文件,准备启动实验一下的时候发现右键并没有 Run 和 Debug 发现了这种方式可解决以上问题,步骤如下: 1. 在idea界面右侧有收起来的工具栏,其中有一个是 Maven ...

Jack088
1分钟前
0
0
freemarker 数字显示问题

freemarker在数字回显时,如果数字比较大,会自动用“,”分割开,然而,此时传递到后台的时候,并不会按照int/long处理,而是当作String字符串处理,所有会报类型不匹配的异常,解决这个问题...

近在咫尺远在天涯
3分钟前
0
0
Java B2B2C多用户商城 springcloud架构 (十八)定时任务(Scheduling Tasks)

这篇文章将介绍怎么通过spring去做调度任务。 构建工程 创建一个Springboot工程,在它的程序入口加上@EnableScheduling,开启调度任务。 @SpringBootApplication@EnableSchedulingpublic ...

itcloud
4分钟前
0
0
Linux TTY、PTS详解

当我们在键盘上敲下一个字母的时候,到底是怎么发送到相应的进程的呢?我们通过ps、who等命令看到的类似tty1、pts/0这样的输出,它们的作用和区别是什么呢? TTY历史 支持多任务的计算机出现...

城市之雾
4分钟前
0
0
Corn 表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month DayofWeek Year或 Seconds Minu...

为了美好的明天
6分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部