在使用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小时(竟然大于一天)。

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

转:Connection reset原因分析和解决方案的更多相关文章

  1. Connection reset原因分析和解决方案

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

  2. Oracle包被锁定的原因分析及解决方案

    http://blog.csdn.net/jojo52013145/article/details/7470812 在数据库的开发过程中,经常碰到包.存储过程.函数无法编译或编译时会导致PL/SQL ...

  3. CMMI5级——原因分析及解决方案(Causal Analysis and Resolution)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010825142/article/details/15338085 聪明的人在出现问题的时候,除了 ...

  4. SQL查询速度慢的原因分析和解决方案

    SQL查询速度慢的原因分析和解决方案 查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建 ...

  5. IP访问频率限制不能用数组循环插入多个限制条件原因分析及解决方案

    14.IP频率限制不能用数组循环插入多个限制条件原因分析及解决方案: define("RATE_LIMITING_ARR", array('3' => 3, '6' => ...

  6. hive on spark:return code 30041 Failed to create Spark client for Spark session原因分析及解决方案探寻

    最近在Hive中使用Spark引擎进行执行时(set hive.execution.engine=spark),经常遇到return code 30041的报错,为了深入探究其原因,阅读了官方issu ...

  7. JDBC Connection Reset问题分析

    2014年7月13日 半年前開始.项目组測试MM在验证功能时.常常报怨讲測试环境上的应用在启动时非常慢,偶尔会报失败,遇到类似问题多数情况下又一次启动一次就能够启动成功,但少数时候也有重复启动不成功的 ...

  8. java.net.SocketException: Connection reset 问题分析

    1. socket编程时容易碰到如下异常: java.net.SocketException: Connection reset by peer: socket write error at java ...

  9. 在Android library中不能使用switch-case语句访问资源ID的原因分析及解决方案

    转自:http://www.jianshu.com/p/89687f618837 原因分析   当我们在Android依赖库中使用switch-case语句访问资源ID时会报如下图所示的错误,报的错误 ...

随机推荐

  1. Java学习总结之方法重载和方法重写

    在学习方法的阶段我学习了方法重载(Overload),而在学习面向对象三大特性之继承的时候我又学习了方法重写(Override).   概念: 方法重载:在同一个类中,允许存在一个以上的同名方法,只要 ...

  2. Maven/Docker快速搭建RocketMQ

    官方文档 [https://rocketmq.apache.org/docs/quick-start/] ①:Bin_二进制安装版 1. 环境准备 系统环境:Centos7 x64 JDK:jdk-8 ...

  3. 开启docker中的mongodb认证授权

    前言: 开启MongoDB服务后,默认是没有权限验证的.直接通过IP加端口就可以远程访问数据库,并对数据库进行任意操作.下面介绍一下如何开启docker中MongoDB的权限认证. 安装完MongoD ...

  4. 深入理解计算机系统 第八章 异常控制流 Part1 第二遍

    第二遍读这本书,每周花两到三小时时间,能读多少读多少(这次看了第 500~507 页,共 8 页) 第一遍对应笔记链接 https://www.cnblogs.com/stone94/p/101651 ...

  5. Docker学习-Docker搭建Consul集群

    1.环境准备 Linux机器三台 网络互通配置可以参考 https://www.cnblogs.com/woxpp/p/11858257.html 192.168.50.21 192.168.50.2 ...

  6. MyBatis批量更新动态sql

    <update id="updateDataKetState"> update ${tablespace}.IDEA_DATAKEY_STATE <trim pr ...

  7. 从最近面试聊聊我所感受的.net天花板

    #0 前言 入职新公司没多久,闲来无事在博客园闲逛,看到园友分享的面试经历,正好自己这段时间面试找工作,也挺多感想的,干脆趁这个机会总结整理一下.博主13年开始实习,14年毕业.到现在也工作五六年了. ...

  8. ArcGIS 发布Feature服务

    运行环境: Win10 ArcGIS10.4 具体操作: 1.打开ArcMap,加载sde中导入的文件,也可以加载shp数据源指向sde中文件 2.保存成mxd,然后点share as-Service ...

  9. goroutiine同步/channel、互斥锁、读写锁、死锁/条件变量

    1. Goroutine同步[数据同步] 为什么需要goroutine同步 gorotine同步概念.以及同步的几种方式 1.1 为什么需要goroutine同步 package main impor ...

  10. python3 之 函数传参

    一.可变对象与不可变对象 在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象. 不可变类型:变量赋值 a=5 后 ...