有关服务端主动关闭socket带来的几个问题分析--tcp四次握手半关闭问题导致
Posted Dreamer who
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有关服务端主动关闭socket带来的几个问题分析--tcp四次握手半关闭问题导致相关的知识,希望对你有一定的参考价值。
一、场景:nginx开启keep-alive:
问题描述:
upstream prematurely closed connection while reading response header from upstream, client: X.90.10, server: www.example.com, request: "POST /web/?a=b HTTP/1.1", upstream: "http://X.32.238:80/web/?a=b", host: "www.example.com"
问题根本原因:keep-alive设置空闲连接数过多,当上游服务器处于tcp半关闭状态的时候,空闲连接被启用发送数据,导致此问题会发生。
解决途径:
(1)keep-alive设置建议尽可能小,需要根据tps设置,目的是让空闲的连接不要碰到那个半关闭的时间点
(2)nginx版本1.15.3增加了配置keepalive_timeout,让nginx主动关闭空闲连接,此值一定要设置的比上游服务器短。
(3)如果nginx版本比较低,可以用http://tengine.taobao.org/document/http_upstream_keepalive_timeout.html或https://github.com/nviennot/nginx-tcp-keepalive模块。
首先必须理解这个keep-alive配置:
nginx此配置描述:
Syntax: keepalive connections;
Default: —
Context: upstream
This directive appeared in version 1.1.4.Activates the cache for connections to upstream servers.
The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed.
It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open. The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well.
一句话:连接池中空闲连接数目,但此数目不影响ningx与上游服务器连接数数目,官方建议此数设置的尽可能小。
经过wireshark服务器命令行抓包,图形界面分析(当时让运维给我抓包分析的,因为线上的问题,机器没权限,主要根据nginx报错的时间慢慢找到的):
此连接空闲了30秒,上游服务器发生了FIN标志(倒数第三行),但是此时此连接被重用发生数据了,由于tcp四次握手处于半关闭状态,上游服务器送了FIN标志包,就不能再向nginx发送数据了,但是还能接受nginx发送数据,所以问题就产生了。
提供了几种解决方案,运维先调小了keep-alive数目,错误不再出现了。
二、用netty做了一个gateway项目,server端(接受web和移动端rest请求)和client端(与上游服务器交互,开启了长连接),当用ab压测的时候,总会卡住,并最终报错:ab (Apache Bench) error: apr_poll: The timeout specified has expired (70007)
项目中通过client端计数统计发生请求数目与返回数目对比,返回数目总是有少数几个没返回(是不是变成黑洞了)。
寻找问题:测试接口增加查询参数a=请求顺序号,返回打印此顺序号,压测结束后,对日志分析,寻找丢失的返回序列号:
cat xx.log.info |grep '==========>>返回序列号'|awk 'print $8'|sort -n | cat -n
上述命令是为了过滤所有的返回序列号并排序,然后用cat -n增加顺序递增序列号,通过对比找到丢失的序列号, 这里找出一个:16916,通过此序列号,我们还必须对经过抓包的数据查询此序列号的请求:
ip.dst==10.2.4.19 && tcp.dstport==80 && http.request.uri contains "a=16916"
主要过滤查询为:http.request.uri contains "a=16916",过滤后的数据:
beaverdeMacBook-Pro.local为本地机器。
通过分析最后 的数据包,上游服务端主动关闭了连接。这条连接基本上没空闲,为什么会关闭呢(不是场景1的现象),我们分析一下上游服务器最近两次的返回http头信息发现:倒数第二次是Connection:keep-alive(上图红线),最后一次是Connection:close(下图红线),代表上游服务器返回后告知网关客户端,此连接马上关闭,不能再利用此连接了,但是后续发现,网关客户端又发生了数据(下图),由于网关客户端没能很好的处理关闭问题,导致了bug的发生,需要根据返回的http包来判断是不是还能重用此长连接。
先列出上面两个场景吧,其实这些都和TCP协议四次握手协议相关,需要抓包分析,然后根据现象分析问题产生原因。
参考:
http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm
以上是关于有关服务端主动关闭socket带来的几个问题分析--tcp四次握手半关闭问题导致的主要内容,如果未能解决你的问题,请参考以下文章
linux网络编程实践:关闭链接存在的问题 TIME_WAIT的2MSL等待