又见CLOSE_WAIT

原文: http://mp.weixin.qq.com/s?__biz=MzI4MjA4ODU0Ng==&mid=402163560&idx=1&sn=5269044286ce1d142cca1b5fed3efab1&3rd=MzA3MDU4NTYzMw==&scene=6#rd

写的太好了,忍不住就转了。

2016-01-19 Anthon Liu FangTalk

抱歉迟了一天发文,本来想昨天出货的。结果又一次被证明我的RPWT,关键时刻生产环境挂了...

原来准备写“分布式系统里的PAC哲学”的,不过这次处理的生产环境的问题还是很有代表性,同时又想起林涛(对!就是那个有百万女朋友的林涛!!!)提过的从一个实际案例出发去写文章的想法,因此就决定从头来写一下这次在线支持的来龙去脉。 (文章有点长,但希望整个思路能对您能有启发)

环境简述

要说清楚问题,先要简单说下生产环境的网络拓扑 (毕竟是个网络问题对吧)

看,挺简单的对吧,一个OpenResty做SLB承受客户端请求,后向代理到几台应用服务器。由于业务要求,必须要同步调用第三方运营商的接口并返回结果到客户端。

怎么“挂”了

深夜接到某妹子电话本该是激动人心的事,但是奈何怎么都高兴不起来,因为来电是来告诉我环境挂了。赶紧问清楚,回答说是一开始响应很慢,后来就彻底拿不到数据了。

好吧,自己摸出手机试下,果然.... 此时夜里11点。

第一反应 (请注意,这里开始是我的排错思路)

从开始打开电脑到电脑点亮的时间里我已经想好了一个初步的排错检查思路, 既然是拿不到数据,那有哪些可能呢?

  • 是不是特例还是所有情况下的数据都获取不到?
  • 是不是网络断了(比如某厂的光缆又断了?)
  • 是不是服务停了 (Sig 11?OOM?或者core dump)
  • 是不是应用服务器都CPU 100%了?
  • 看看监控系统有没有报警? (当然得有对吧)
  • 看看DB是不是被人删了?(进过某旅游网站的事件后,这总也是一种可能行对吧)

好,因为我们有云监控,看了下

  • SLB的心跳还活着,排除网络问题
  • 所有服务器的CPU/Memory/IO都还正常没有峰值
  • 关键进程还在
  • DB也还健在(还好 还好)

开始检查

既然初步排除上述的问题,那下一步基本就是SSH到服务器上去看情况了。 自然从网络开始,这里要想说给很多在做或者即将做在线生产环境支持的小伙伴说的第一句话: “先听听操作系统的声音,让操作系统来告诉你问题在哪”。 不论是windows和Linux都提供了一堆小工具小命令,在过度依赖安装第三方工具前请先看看是否操作系统自带的工具已经不够支撑你了。

好,第一个检查就是本机的网络连接

netstat -anop tcp

结果

.......此处省略100多行.....

我擦,close_wait又让我撞见了. 看了几台应用服务器都是上百个close_wait. (加起来有近千个close_wait, 发财了)。

网上有太多文章描述这个东西了所以我不会展开去解释,就浓缩成以下几点,大家参考这图理解

  • close_wait 是TCP关闭连接过程中的一个正常状态
  • close_wait只会发生在关闭链接的那一端(各位亲,请不要把图里的client/server和项目里的客户端服务器端混淆)
  • close_wait除非你杀进程,close_wait是不会自动消失的。当然不消失意味着占着资源呢,这里就是占着FD。

看到这里基本拿不到数据的原因之一找到了,大量的close_wait. 我之前项目也见过有的开发见到这种情况的直觉反应就是重启大法,其实也不能算这个做法有错,毕竟这个时候服务当了,客户疯了,夜已深了,你想休息了。 但,这样真的对吗?

“停” 先别急着重启

如果你这时候重启了,的确立竿见影解决了当前问题,但你却失去真正解决问题的机会。这就是我想说的第二句话: “保留一下现场,不是所有问题的根源都能从日志里找得到的”。 close_wait 绝对就是这类问题,如果你是一位有过类似经历的开发或者DevOps,你到现在应该有了下面2个疑问:

  1. 为啥一台机器区区几百个close_wait就导致不可继续访问?不合理啊,一台机器不是号称最大可以开到65535个端口吗?
  2. 为啥明明有多个服务器承载,却几乎同时出了close_wait? 又为什么同时不能再服务?那要SLB还有啥用呢

好,这也是我当时的问题,让我们继续往下分析:

  • 先理顺出现close_wait的链接流向

前面说过close_wait是关闭连接过程中的正常状态,但是正常情况下close_wait的状态很快就会转换所以很难被捕捉到。所以如果你能发现大批量的close_wait基本可以确定是出问题了。那第一个要确定的自然是连接的流向,判断依据很简单(还是netstat -anop tcp)


命令返回里有一栏Foreign Address,这个就是代表对方的IP地址,这个时候再结合上面那张TCP的握手图,我们就知道是哪台机器和你连接着但是却主动关闭了连接。

  • 根据项目数据请求流向还原可能的场景

知道了是哪台IP,那接下来就可以根据项目实际情况还原连接的场景。在我这里所有的close_wait都发生在和SLB的连接上。因此说明,是SLB主动关闭了连接但是多台应用服务器都没有响应ack 导致了close_wait. 只是这样够吗? 明显不够。继续

SLB 作为负载均衡,基本没有业务逻辑,那它会主动关闭连接的场景有哪些?

  • 进程退出(正常或者非正常)
  • TCP 连接超时

这2个情况很好判断而且大多数情况下是第二种(我遇见的也是),如果你还记得我文章一开始的环境结构图,我想基本可以得出以下结论是:

由于调用第三方的请求API太慢而导致SLB这边请求超时引起的SLB关闭了连接.

那解决方案也很容易就有了:

  • 加大SLB到应用服务器的连接超时时间
  • 在调用第三方的时候采用异步请求

完了吗? (我怎么那么啰嗦。。。)

“再等等” 还有问题没被回答

我们还有两个问题没回答:

  1. 为啥一台机器区区几百个close_wait就导致不可继续访问?不合理啊,一台机器不是号称最大可以开到65535个端口吗?
  2. 为啥明明有多个服务器承载,却几乎同时出了close_wait? 又为什么同时不能再服务?那要SLB还有啥用呢

是啊,解释了为什么出close_wait, 但并不能解释这2个问题。好吧,既然找到了第一层原因,就先重启服务让服务可以用吧。剩下的我们可以用个简单的原型代码模拟一下。此时我的目光回到了我们用的Tornado上面来,“当你有问题解释不了的时候,你还没有发现正真的问题”

Tornado是一个高性能异步非阻塞的HTTP 服务器(还不明白这个啥意思的可以看 “从韩梅梅和林涛的故事中,学习一下I/O模型 ” 这篇文章,生动!!!),其核心类就是IOLoop,默认都是用HttpServer单进程单线程的方式启动 (Tornado的process.fork_processes 也支持多进程方式,但官方并不建议)。我们还是通过图来大概说下

IOLoop干了啥:

  • 维护每个listen socket注册的fd;
  • 当listen socket可读时回调_handle_events处理客户端请求,这里面的实现就是基于epoll 模型

好,现在我们知道:

  1. Tornado是单进程启动的服务,所以IOLoop也就一个实例在监听并轮询
  2. IOLoop在监听端口,当对应的fd ready时会回调注册的handler去处理请求,这里的handler就是我们写业务逻辑的RequestHandler
  3. 如果我们启用了Tornado的  @tornado.gen.coroutine,那理论上一个请求很慢不会影响其他的请求,那一定是代码什么地方错了。

进而查看实现代码,才真相大白,虽然我们用了 @tornado.gen.coroutine 和yield,但是在向第三方请求时用的是urllib2 库。这是一个彻头彻尾的同步库,人家就不支持AIO(Tornado 有自己AsyncHTTPClient支持AIO).

由此让我们来总结下原因:

  1. Tornado是单进程启动的服务,所以IOLoop也就一个实例在监听并轮询
  2. Tornado在bind每个socket的时候有默认的链接队列(也叫backlog)为128个
  3. 由于代码错误,我们使用了同步库urllib2 做第三方请求,导致访问第三方的时候当前RequestHandler是同步的(yield不起作用),因此当IOLoop回调这个RequestHandler时会等待它返回
  4. 第三方接口真的不快!

最后来回答这两个问题:

1. 为啥一台机器区区几百个close_wait就导致不可继续访问?不合理啊,一台机器不是号称最大可以开到65535个端口吗?

[回答]: 由于原因#4和#3所以导致整个IOLoop慢了,进而因为#2 导致很多请求堆积,也就是说很多请求在被真正处理前已经在backlog里等了一会了。导致了SLB这端的链接批量的超时,同时又由于close_wait状态不会自动消失,导致最终无法再这个端口上创建新的链接引起了停止服务

2. 为啥明明有多个服务器承载,却几乎同时出了close_wait? 又为什么同时不能再服务?那要SLB还有啥用呢

[回答]: 有了上一个答案,结合SLB的特性,这个也就很好解释。这就是所谓的洪水蔓延,当SLB发现下面的一个节点不可用时会吧请求routing到其他可用节点上,导致了其他节点压力增大。也犹豫相同原因,加速了其他节点出现close_wait.

写在最后

花了2个小时写完这文章,意图并不是在重复去解释什么是close_wait. 只是希望藉由一次生产环境的实战给需要做支持的小伙伴们一个解决问题思路的参照。一次支持也许是快速且短暂的,解决的快不代表你真的就理解了里面的原因,有时候新的知识恰恰就藏在一次次的环境问题当中。

时间: 2024-10-18 05:57:29

又见CLOSE_WAIT的相关文章

【服务器】一次对Close_Wait 状态故障的排查经历

最近接连听说一台线上服务器总是不响应客户端请求. 登录服务器后查询iis状态,发现应用程序池状态变为已停止. 按经验想,重启后应该就ok,第一次遇到也确实起了作用,当时完全没在意,以为是其他人无意把服务关闭了而已. 但是之后几天几乎每天都出现问题,应用程序池再次成为 已停止 状态.这个情况显然有问题.于是开始排查设置. 线上环境很简单,iis + API应用,数据库在内网上,没有反向代理. 出问题的应用程序池承载了一个基础数据API,查询量极小,数据量也极小,只是因为同事做实现时提过,查询很麻烦

CLOSE_WAIT问题-TCP

环境简述 要说清楚问题,先要简单说下生产环境的网络拓扑(毕竟是个网络问题对吧) 看,挺简单的对吧,一个OpenResty做SLB承受客户端请求,反响代理到几台应用服务器.由于业务要求,必须要同步调用第三方运营商的接口并返回结果到客户端. 怎么"挂"了 深夜接到某妹子电话本该是激动人心的事,但是奈何怎么都高兴不起来,因为来电是来告诉我环境挂了.赶紧问清楚,回答说是一开始响应很慢,后来就彻底拿不到数据了. 好吧,自己摸出手机试下,果然.... 此时夜里11点. 第一反应(请注意:这里开始是

17-又见01背包

/*                                        又见01背包时间限制:1000 ms  |  内存限制:65535 KB难度:3 描述        有n个重量和价值分别为wi 和 vi 的 物品,从这些物品中选择总重量不超过 W     的物品,求所有挑选方案中物品价值总和的最大值.    1 <= n <=100    1 <= wi <= 10^7    1 <= vi <= 100    1 <= W <= 10^

bzoj3620: 似乎在梦中见过的样子

3620: 似乎在梦中见过的样子 Description “Madoka,不要相信 QB!”伴随着 Homura 的失望地喊叫,Madoka 与 QB 签订了契约. 这是 Modoka 的一个噩梦,也同时是上个轮回中所发生的事.为了使这一次 Madoka 不再与 QB签订契约,Homura 决定在刚到学校的第一天就解决 QB.然而,QB 也是有许多替身的(但在第八话中的剧情显示它也有可能是无限重生的),不过,意志坚定的 Homura 是不会放弃的——她决定 消灭所有可能是 QB 的东西.现在,她

nyoj 814 又见拦截导弹

又见拦截导弹 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 大家对拦截导弹那个题目应该比较熟悉了,我再叙述一下题意:某国为了防御敌国的导弹袭击,新研制出来一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.突然有一天,雷达捕捉到敌国的导弹来袭.由于该系统存在缺陷,所以如果想把所有的导弹都拦截下来,就要多准备几套这样的导弹拦截系统.但是由于该系统成本太高,所以为了降低成本,请你计算一下最少需要

张书乐:活久见!王者荣耀不是农药!拜托,这不过是一个游戏

今日午后,在香港上市的腾讯控股一度跌5%,目前跌幅近4%.人民网日前发表评论文章称,<王者荣耀>面向社会不断在释放负能量,监管主体有必要让游戏多一些"善意".在很多人看来,这一次是人民网抨击王者荣耀. 我想说,在我看来,这真不是抨击,而是希望能够有一个更好的无毒"农药",它呼吁的是游戏厂商的监管,也呼吁的是来自社会各界以及有关部门的监管-- 文/张书乐(游戏产业资深时评人) 作为一个2004年就开始做游戏产业评论的撰稿人,当看到这样的议论时,真的有一种&

HDU 2504 又见GCD(最大公约数与最小公倍数变形题)

又见GCD Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 18480    Accepted Submission(s): 7708 Problem Description 有三个正整数a,b,c(0<a,b,c<10^6),其中c不等于b.若a和c的最大公约数为b,现已知a和b,求满足条件的最小的c. Input 第一行输入一个n

HDU2504 又见GCD

又见GCD Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9984    Accepted Submission(s): 4157 Problem Description 有三个正整数a,b,c(0<a,b,c<10^6),当中c不等于b.若a和c的最大公约数为b,现已知a和b,求满足条件的最小的c. Input 第一行输入一个n,

netstat -pnat 出现大量的CLOSE_WAIT

关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况.前者是指有本地主机主动发起的关闭:而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接.将关闭部分的状态转移摘出来,就得到了下图: 产生原因通过图上,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢?在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态.通常来讲,CLOSE_WAIT状态的持续时间