详解linux中的backlog


什么是backlog

backlog是linux下socket函数之listen的参数,当应用程序调用listen系统调用让一个socket进入LISTEN状态时,须要指定一个backlog参数。这个参数常常被描述为,新链接队列的长度限制。html

因为TCP创建链接须要进行3次握手,一个新链接在到达ESTABLISHED状态能够被accept系统调用返回给应用程序前,必须通过一个中间状态SYN RECEIVED。这意味着,TCP/IP协议栈在实现backlog队列时,有两种不一样的选择:linux

  • 仅使用一个队列,队列规模由listen系统调用backlog参数指定。当协议栈收到一个SYN包时,响应SYN/ACK包而且将链接加进该队列。当相应的ACK响应包收到后,链接变为ESTABLISHED状态,能够向应用程序返回。这意味着队列里的链接能够有两种不一样的状态:SEND RECEIVED和ESTABLISHED。只有后一种链接才能被accept系统调用返回给应用程序。bootstrap

  • 使用两个队列——SYN队列(待完成链接队列)和accept队列(已完成链接队列)。状态为SYN RECEIVED的链接进入SYN队列,后续当状态变动为ESTABLISHED时移到accept队列(即收到3次握手中最后一个ACK包)。顾名思义,accept系统调用就只是简单地从accept队列消费新链接。在这种状况下,listen系统调用backlog参数决定accept队列的最大规模。服务器

对于linux操做系统,内核在2.2以后的版本,tcp/ip协议实现了第二种方案,即一个syn队列,一个accept队列,syn队列的长度由系统级别设置,accept队列的长度能够由应用级别设置,tcp建连交互流程以下图所示:网络

  1. client 端使用 connect() 向 server 端发起链接请求(发送 syn 包),此时 client 端的 TCP 的状态为 SYN_SENT。并发

  2. server 端在收到 syn 包后,将 TCP 相关信息放到 syn queue(半链接队列)中,同时向 client 发送 syn+ack,server 端 TCP 的状态为 SYN_RCVD。socket

  3. client 端收到 server 端的 syn+ack 后,向 server 端发送 ack,此时 client 端的 TCP 的状态为 ESTABLISHED。Server 端收到 ack 确认后,从 syn queue 里将 TCP 信息取出,并放到 accept queue(全链接队列)中,此时 server 端的 TCP 的状态为 ESTABLISHED。tcp

通过以上三个过程,client 端和 server 端创建了链接,即所谓三次握手的过程。
由此,咱们已经知道了什么是backlog,以及半链接队列、全链接队列的由来。下文当中提到的backlog均表示全链接队列长度。那怎么设置backlog的值呢?


如何设置backlog

backlog的大小有两方面因素决定,一是系统层面,二是应用层面
  1. 系统层面:somaxconn参数,能够经过编辑/proc/sys/net/core/somaxconn的值进行设置函数

  2. 应用层面:对于netty服务端来讲,经过serverbootstrap的option进行设置,即option((ChannelOption.SO_BACKLOG,number),number即为要设置的大小,类型为int高并发

backlog最终的取值为两者中的最小值,即min(backlog,somaxconn),在服务启动以后,咱们能够经过,ss -tnlp进行查看,以下图所示:

在listen状态状况下:Recv-Q表示存在于backlog当中未被服务端应用程序accept的队列大小;Send-Q表示最大的backlog的大小,即咱们设置的backlog的大小min(backlog,somaxconn)。那当设置backlog时应参考怎么的设置标准呢?

backlog设置标准

在设置backlog时,既不能太大,也不能过小,设置太大,当访问流量忽然增长超过服务器的负载时,客户端不能快速失败,形成读取链接超时,对服务端来说,会影响网络I/O,同时形成内存使用过大,cpu负载增长;若是设置过小,不能充分发挥服务端的负载能力,而且会客户端形成链接失败的状况。应根服务端可以承受的最大qps进行设置,backlog的大小,应设置成在服务端最大可以承受qps的1-1.5倍左右。


backlog使用分析

对于backlog队列的使用状况,咱们能够经过netstat进行查询,以下图所示:

如图所示,咱们能够看到当前backlog已使用的队列长度为1。由以上分析咱们能够知道,处于backlog队列中的链接,还未被应用线程accept。也就是说咱们能够经过netstat查询到当前链接,但该链接并未关联进程pid。分析图中全部链接,咱们能够知道,端口号为34328未关联pid,表明此链接处于全链接队列当中。
固然咱们也能够经过ss命令进行查询当前监听端口号的backlog使用状况,以下图所示:

如图所示,当前监听端口全链接队列长度为2,当全链接队列的使用长度为(backlog+1)时,表明次全链接队列已满,那当全链接队列已满的状况下,当又有新建链接请求进来时会怎么处理呢?
当再有新的创建链接的请求是,会根据服务端的/proc/sys/net/ipv4/tcp_abort_on_overflow设置而进行不一样的处理。
  • 当tcp_abort_on_overflow=0,直接丢弃该ACK,经过tcpdump抓包,能够看到以下的交互流程

此时服务端处于 【syn_rcvd】的状态,客户端处于 【established】的状态:

  • 当tcp_abort_on_overflow=1,发送RST通知client,client会报connection reset by peer

抓包查看其流程以下所示:

当全链接队列溢出时,有哪些指标能够说明呢,咱们又有那可有效的查询手段呢?咱们能够经过netstat -s命令进行查询:

如图所示,4879 times表示全链接队列溢出的次数,隔几秒查询一次,若是这个数字一直在递增,说明全链接队列出现了溢出的状态。
咱们又怎么肯定究竟是哪一个监听端口出现了全链接队列溢出的状况呢?
  1. 经过ss -tnlp 查询监听端口全链接队列的使用状况。

  2. 经过netstat查询出链接状态处于established、但未关联进程号的链接,此链接对应着源端口以及目标端口,此链接的源端口就是咱们应用程序监听的端口,即出现backlog队列溢出的端口号。

总之,对于咱们平常工做当中,不管是长链接,短链接,以及各类的消息中间件,只要牵涉到监听端口,都会牵涉到backlog,当咱们了解backlog以后,对咱们处理网络问题,已经如何提交系统的高并发将大有裨益。


参考文献

http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html
https://www.jianshu.com/p/7fde92785056

做者简介

李双全,民生科技有限公司用户体验技术部Firefly移动金融开发平台Java开发工程师。

相关文章
相关标签/搜索