什么是backlog
backlog是linux下socket函数之listen的参数,当应用程序调用listen系统调用让一个socket进入LISTEN状态时,需要指定一个backlog参数。这个参数经常被描述为,新连接队列的长度限制。
由于TCP建立连接需要进行3次握手,一个新连接在到达ESTABLISHED状态可以被accept系统调用返回给应用程序前,必须经过一个中间状态SYN RECEIVED。这意味着,TCP/IP协议栈在实现backlog队列时,有两种不同的选择:
仅使用一个队列,队列规模由listen系统调用backlog参数指定。当协议栈收到一个SYN包时,响应SYN/ACK包并且将连接加进该队列。当相应的ACK响应包收到后,连接变为ESTABLISHED状态,可以向应用程序返回。这意味着队列里的连接可以有两种不同的状态:SEND RECEIVED和ESTABLISHED。只有后一种连接才能被accept系统调用返回给应用程序。
使用两个队列——SYN队列(待完成连接队列)和accept队列(已完成连接队列)。状态为SYN RECEIVED的连接进入SYN队列,后续当状态变更为ESTABLISHED时移到accept队列(即收到3次握手中最后一个ACK包)。顾名思义,accept系统调用就只是简单地从accept队列消费新连接。在这种情况下,listen系统调用backlog参数决定accept队列的最大规模。
对于linux操作系统,内核在2.2之后的版本,tcp/ip协议实现了第二种方案,即一个syn队列,一个accept队列,syn队列的长度由系统级别设置,accept队列的长度可以由应用级别设置,tcp建连交互流程如下图所示:
client 端使用 connect() 向 server 端发起连接请求(发送 syn 包),此时 client 端的 TCP 的状态为 SYN_SENT。
server 端在收到 syn 包后,将 TCP 相关信息放到 syn queue(半连接队列)中,同时向 client 发送 syn+ack,server 端 TCP 的状态为 SYN_RCVD。
client 端收到 server 端的 syn+ack 后,向 server 端发送 ack,此时 client 端的 TCP 的状态为 ESTABLISHED。Server 端收到 ack 确认后,从 syn queue 里将 TCP 信息取出,并放到 accept queue(全连接队列)中,此时 server 端的 TCP 的状态为 ESTABLISHED。
如何设置backlog
系统层面:somaxconn参数,可以通过编辑/proc/sys/net/core/somaxconn的值进行设置
应用层面:对于netty服务端来说,通过serverbootstrap的option进行设置,即option((ChannelOption.SO_BACKLOG,number),number即为要设置的大小,类型为int
backlog最终的取值为二者中的最小值,即min(backlog,somaxconn),在服务启动之后,我们可以通过,ss -tnlp进行查看,如下图所示:
backlog设置标准
backlog使用分析
对于backlog队列的使用情况,我们可以通过netstat进行查询,如下图所示:
当tcp_abort_on_overflow=0,直接丢弃该ACK,通过tcpdump抓包,可以看到如下的交互流程
当tcp_abort_on_overflow=1,发送RST通知client,client会报connection reset by peer
抓包查看其流程如下所示:
通过ss -tnlp 查询监听端口全连接队列的使用情况。
通过netstat查询出连接状态处于established、但未关联进程号的连接,此连接对应着源端口以及目标端口,此连接的源端口就是我们应用程序监听的端口,即出现backlog队列溢出的端口号。