小记

# tcp三次握手
tcp标志位有6种标示: SYN(synchronous建立联机) ACK(acknowledgement确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)

第一次握手: client ---------------> server    # client发送后状态为:SYN_SEND server接受后状态为:SYN_RCVD server收到请求后会将这个请求放到syns queue队列中
client发送位码为 [SYN=1,seq number= x(随机产生)] 的数据包到server,server由SYN=1得知client要求建立联机
第二次握手: server ---------------> client    # client收到SYN+ACK将状态改为:ESTABLISHED(半连接状态)
server收到client建立联机的请求后,向client发送[ack number=x + 1(client的seq number+1),SYN=1,ACK=1,seq number= y(随机产生)]的数据包
第三次握手: client ---------------> server    # server收到ACK将状态修改为:ESTABLISHED 并把该请求从syns queue队列放到accept queue队列
client收到数据包后检查ack number是否正确(即client第一次发送的seq number+1),以及位码是否为ACK=1,如果正确,client会发送 [ack number=y+1(server的seq number+1),ACK=1]数据包给server,server接受之后确认ack number=y+1值与ACK=1,连接建立成功

# syns queue队列: 用于保存半连接状态的请求
队列大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,著名的SYN洪水攻击就是建立大量的半连接状态请求,然后丢弃,导致syns queue不能保存正常的请求,注意半连接队列长度不能超过全连接队列长度

# accept queue队列: 用于保存全连接状态的请求
队列大小通过/proc/sys/net/core/somaxconn指定,net.core.somaxconn 定义了系统级别的全连接队列最大长度,backlog 只是应用层传入的参数,不可能超过内核参数,所以 backlog 必须小于等于 net.core.somaxconn

# backlog 的定义是已连接但未进行 accept 处理的 SOCKET 队列大小
backlog 是底层方法 int listen 的一个参数,Nginx/Tomcat 等这种 Web 服务器,都提供了 backlog 参数设置入口
只增大应用层 backlog 参数大小是没有意义的,因为可能内核参数关于连接队列设置的都很小,必须综合应用层和内核参数一起调整
system
net.core.somaxconn = 65535 # 默认为128,定义系统中每一个端口最大的监听队列长度
net.core.netdev_max_backlog = 65535 # 默认为1000,请求被切换到CPU处理前被网卡缓存的速率包,根据网卡文档加大值可以提高性能
net.ipv4.tcp_max_syn_backlog = 65535 # 默认为1024,对于还未获得对方确认的连接请求,可保存在syns queue队列中的最大数目

前端 Nginx/Tomcat
listen 80 backlog=65535; 

后台 PHP
listen.backlog = 65535

# 这里还有一个需要注意的点
如果机器的性能不高,我们依然增大 backlog 参数和内核连接队列,反而会适得其反
假设 PHP-fpm 的 QPS(每秒响应次数) 是 5000,那么处理完 65535 个请求大概需要 13 秒
但是前端 Nginx 和 PHP-fpm 的连接已经等待超时,当 PHP-fpm 处理完最后一个请求,再往这个 SOCKET ID 写数据时,却发现连接已经关闭,得到的是"error: Broken Pipe"
这也是为什么 2013 年 12 月 14 日发布的 PHP5.5.6 中 backlog 参数被修改为 65535,后来在 2014 年 7 月 22 日又修改为了 511