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

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

深入ThreadLocal之二的更多相关文章

  1. 理解ThreadLocal(之二)

    想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...

  2. 深入理解ThreadLocal(二)

    3 InheritableThreadLocal的使用 通过上面的分析知道通过ThreadLocal保存的值是线程隔离的.其实在Thread对象中,还有一个ThreadLocal.ThreadLoca ...

  3. ThreadLocal (二):什么时候使用 InheritableThreadLocal

    一.ThreadLocal 在父子线程传递的问题 public class InheritableThreadLocalDemo { // 全局变量 // static ThreadLocal< ...

  4. 切换数据库+ThreadLocal+AbstractRoutingDataSource 一

    最近项目用的数据库要整合成一个,所以把多源数据库切换的写法要清除掉.所以以下记载了多远数据库切换的用法及个人对源码的理解. 框架:Spring+mybatis+vertx,(多源数据库切换的用法不涉及 ...

  5. 五、线程本地ThreadLocal

    一.线程私有 在多线程情况下,对于一个共享的数据可能会产生线程安全问题.最简单的解决办法就是堆访问共享数据的时候加锁,但我们知道加锁是很影响效率的,尤其是像数据库连接这样耗费资源较多的情况下,加锁就意 ...

  6. ThreadLocal (三):为何TransmittableThreadLocal

    一.示例 线程池内的线程并没有父子关系,所以不适合InheritableThreadLocal的使用场景 public class ThreadPoolInheritableThreadLocalDe ...

  7. 从零开发分布式数据库中间件 二、构建MyBatis的读写分离数据库中间件

    在上一节 从零开发分布式数据库中间件 一.读写分离的数据库中间件 中,我们讲了如何通过ThreadLocal来指定每次访问的数据源,并通过jdbc的连接方式来切换数据源,那么这一节我们使用我们常用的数 ...

  8. ThreadLocal的使用场景分析

    目录 一.ThreadLocal介绍 二.使用场景1——数据库事务问题 2.1 问题背景 2.2 方案1-修改接口传参 2.3 方案2-使用ThreadLocal 三.使用场景2——日志追踪问题 四. ...

  9. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

随机推荐

  1. Compute Mean Value of Train and Test Dataset of Caltech-256 dataset in matlab code

    Compute Mean Value of Train and Test Dataset of Caltech-256 dataset in matlab code clc;imPath = '/ho ...

  2. Java 性能优化

    http://eclipsesource.com/blogs/2013/01/21/10-tips-for-using-the-eclipse-memory-analyzer/ http://docs ...

  3. 安装pgadmin3

    在centos/redhat/系统是x86_64 1.下载适合自己系统的合适的版本http://dl.fedoraproject.org/pub/epel/6/x86_64/ ( yum instal ...

  4. 配置 Hdp 4 Window 中的一些问题

    1,E0508: User [?] not authorized for WF job [-- jobid] 很明显验证问题, 修改 oozie-site.xml中节点为 <property&g ...

  5. 虚拟化之lxc

    LXC 中文名称就是 Linux 容器工具,容器可以提供轻量级的虚拟化,以便隔离进程和资源,使用 LXC 的优点就是不需要安装太多的软件包,使用过程也不会占用太多的资源,本文循序渐进地介绍LXC的建立 ...

  6. TKinter之输入框

    输入框是 Entry,应用程序要取得用户的信息,输入框是必不可少的. 输入框比较重要的一个函数: get:返回值即输入框的内容 比如e是一个输入框,e['show']='*'就变成了密码框 小例子:用 ...

  7. 【性能诊断】三、单功能场景的性能分析(RedGate Profiler)

    上一篇我们简单的对客户前端和数据库后端的性能问题进行了定位,如果排除了这两块,问题基本就确定在应用服务器上.但是我们往往对应用服务器,或者说应用程序的性能最陌生,一旦出现性能问题往往有无所适从的感觉, ...

  8. NET 2.0 OCR文字识别技术(Tesseract 引擎)[转]

    一.OCR简介  参见http://baike.baidu.com/view/17761.htm?fr=ala0_1  大家参照,我第一次也是这么了解的,呵呵.高手见笑 现在市面上好多OCR 引擎,不 ...

  9. [hadoop] WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

    hadoop 启动后,有警告信息: WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform ...

  10. MySql服务基础

           MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 ...