一、TCP 3次握手
可分为4步
1 客户端发起connect(),发送SYN j
2 服务器从SYN queue中建立条目,响应SYN k, ACK J+1
3 客户端connect()成功返回,响应ACK K+1
4 服务器将socket从SYN queue移入accept queue,accept()成功返回
注:SYN/FIN各占一个序列号,ACK/RST不占序列号
listen address[:port] [backlog=number]
,参数backlog 限制了用于存放处于挂起状态连接的队列最大长度, 已连接但未进行accept处理的SOCKET队列大小,即这些连接已经完全建立了,但还没有被处理,其默认值是 -1。当一个连接请求到达时,如果此时队列满了,客户端会收到连接拒绝(“Connection refused”)。
php-fpm的backlog大小设置跟php-fpm的处理能力有关
1、backlog太大了
,导致php-fpm处理不过来,nginx那边等待超时,断开连接,报504 gateway timeout错。同时php-fpm处理完准备write 数据给nginx时,发现TCP连接断开了,报“Broken pipe”。
2、php-fpm的backlog太小
的话,nginx之类的client请求,根本进入不了php-fpm的accept queue,报“502 Bad Gateway”错。所以,这还得去根据php-fpm的QPS来决定backlog的大小。计算方式最好为QPS=backlog。
SYN queue长度由tcp_max_syn_backlog指定,accept queue则由net.core.somaxconn决定,listen(fd, backlog)的backlog上限由somaxconn决定.
backlog=number
sets the backlog parameter in the listen() call that limits the maximum length for the queue of pending connections. By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, and to 511 on other platforms.
在linux 2.2以前,backlog大小包括了半连接状态和全连接状态两种队列大小。linux 2.2以后,分离为两个backlog来分别限制半连接SYN_RCVD状态的未完成连接队列
大小跟全连接ESTABLISHED状态的已完成连接队列
大小。
互联网上常见的TCP SYN FLOOD恶意DOS攻击方式就是用
/proc/sys/net/ipv4/tcp_max_syn_backlog
来控制的。
在使用listen函数时,内核会根据传入参数的backlog跟系统配置参数/proc/sys/net/core/somaxconn
中,二者取最小值,作为“ESTABLISHED状态之后,完成TCP连接,等待服务程序ACCEPT”的队列大小。在kernel 2.4.25之前,是写死在代码常量SOMAXCONN,默认值是128。在kernel 2.4.25之后,在配置文件/proc/sys/net/core/somaxconn
(即 /etc/sysctl.conf
之类 )中可以修改。
客户端connect()返回不代表TCP连接建立成功,有可能此时服务器accept queue已满,OS会直接丢弃后续ACK请求;
客户端以为连接已建立,开始后续调用(譬如send)等待直至超时;
服务器则等待ACK超时,会重传SYN k, ACK J+1给客户端(重传次数受限net.ipv4.tcp_synack_retries);
注:accept queue溢出,即便SYN queue没有溢出,新连接请求的SYN也可能被drop
二、How TCP backlog works in linux
在How TCP backlog works in linux一文中,作者给出了比较详细的分析:
1、第一种实现方式
在底层维护一个由backlog指定大小的队列。服务端收到SYN后,返回一个SYN/ACK,并把连接放入队列中,此时这个连接的状态是SYN_RECEIVED。当客户端返回ACK后,此连接的状态变为ESTABLISHED。队列中只有ESTABLISHED状态的连接能够交由应用处理。第一种实现方式可以简单概括为:一个队列,两种状态。
2、第二种实现方式
在底层维护一个SYN_RECEIVED队列和一个ESTABLISHED队列,当SYN_RECEIVED队列中的连接返回ACK后,将被移动到ESTABLISHED队列中。backlog指的是ESTABLISHED队列的大小。
传统的基于BSD的tcp实现第一种方式,在linux2.2之前,内核也实现第一种方式。当队列满了以后,服务端再收到SYN时,将不会返回SYN/ACK。比较优雅的处理方法就是不处理这条连接,不返回RST,让客户端重试。
在linux2.2后,选择第二种方式实现,SYN_RECEIVED队列的大小由/proc/sys/net/ipv4/tcp_max_syn_backlog
系统参数指定,ESTABLISHED队列由backlog和/proc/sys/net/core/somaxconn
中较小的指定。
三、如何查看accept queue溢出:
netstat –s | grep LISTEN返回*** SYNS to LISTEN sockets ignored
参考
1、Module ngx_http_core_module
2、TCP三次握手之backlog
3、CP三次握手-backlog队列问题
4、TCP SOCKET中backlog参数的用途是什么?推荐
5、关于PHP-FPM的backlog的默认值推荐
6、nginx配置个https+backlog+sysctl.conf
7、Backlog 讨论
8、How TCP backlog works in Linux
9、再理解tcp backlog