理解 Linux backlog/somaxconn 内核参数

引言

之前负载均衡超时问题这篇博文中提到一个可能原因是:
后端服务器 Socket accept 队列满,系统的 somaxconn 内核参数默认太小。
下面我们我深入学习理解下 somaxconn 内核参数相关内容。

TCP SYN_REVD, ESTABELLISHED 状态对应的队列

TCP 建立连接时要经过 3 次握手,在客户端向服务器发起连接时,
对于服务器而言,一个完整的连接建立过程,服务器会经历 2 种 TCP 状态:SYN_REVD, ESTABELLISHED。

对应也会维护两个队列:

  1. 一个存放 SYN 的队列(半连接队列)
  2. 一个存放已经完成连接的队列(全连接队列)

当一个连接的状态是 SYN RECEIVED 时,它会被放在 SYN 队列中。
当它的状态变为 ESTABLISHED 时,它会被转移到另一个队列。
所以后端的应用程序只从已完成的连接的队列中获取请求。

如果一个服务器要处理大量网络连接,且并发性比较高,那么这两个队列长度就非常重要了。
因为,即使服务器的硬件配置非常高,服务器端程序性能很好,
但是这两个队列非常小,那么经常会出现客户端连接不上的现象,
因为这两个队列一旦满了后,很容易丢包,或者连接被复位。
所以,如果服务器并发访问量非常高,那么这两个队列的设置就非常重要了。

Linux backlog 参数意义

对于 Linux 而言,基本上任意语言实现的通信框架或服务器程序在构造 socket server 时,都提供了 backlog 这个参数,
因为在监听端口时,都会调用系统底层 API: int listen(int sockfd, int backlog);

listen 函数中 backlog 参数的定义如下:

Now it specifies the queue length for completely established sockets waiting to be accepted,
instead of the number of incomplete connection requests.
The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog sysctl.
When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default),
it is silently truncated to SOMAXCONN.

backlog 参数描述的是服务器端 TCP ESTABELLISHED 状态对应的全连接队列长度。

全连接队列长度如何计算?
如果 backlog 大于内核参数 net.core.somaxconn,则以 net.core.somaxconn 为准,
即全连接队列长度 = min(backlog, 内核参数 net.core.somaxconn),net.core.somaxconn 默认为 128。
这个很好理解,net.core.somaxconn 定义了系统级别的全连接队列最大长度,
backlog 只是应用层传入的参数,不可能超过内核参数,所以 backlog 必须小于等于 net.core.somaxconn。

半连接队列长度如何计算?
半连接队列长度由内核参数 tcp_max_syn_backlog 决定,
当使用 SYN Cookie 时(就是内核参数 net.ipv4.tcp_syncookies = 1),这个参数无效,
半连接队列的最大长度为 backlog、内核参数 net.core.somaxconn、内核参数 tcp_max_syn_backlog 的最小值。
即半连接队列长度 = min(backlog, 内核参数 net.core.somaxconn,内核参数 tcp_max_syn_backlog)。
这个公式实际上规定半连接队列长度不能超过全连接队列长度。

其实,对于 Nginx/Tomcat 等这种 Web 服务器,都提供了 backlog 参数设置入口,
当然它们都会有默认值,通常这个默认值都不会太大(包括内核默认的半连接队列和全连接队列长度)。
如果应用并发访问非常高,只增大应用层 backlog 是没有意义的,因为可能内核参数关于连接队列设置的都很小,
一定要综合应用层 backlog 和内核参数一起看,通过公式很容易调整出正确的设置。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
还没读完,作为备份。 sys_socket() --->retval = sock_create(family, type, protocol, &sock;) 1.*获取套接字的地址 sock = sock_alloc(); 2.1128 /*设置类型*/ 1129 sock->type = type; 3.1142 * 获取具体传输层的创建函数,不同协议族不同 1143 * pf对应具体的inet_family_ops 1147 pf = rcu_dereference(net_families[family]); 4.1162 * 调用传输层的inet_create() 1164 err = pf->create(sock, protocol); -------------> static int inet_create(struct socket *sock, int protocol) 1. 260 /*设置链接状态*/ 261 sock->state = SS_UNCONNECTED; 2. 269 *查找对应的协议及其操作 272 list_for_each_rcu(p, &inetsw;[sock->type]) 293 * 如果没有找到,则继续加载,设置条件,若条件满足则 294 * 返回上面继续查找。最多可以反复加载2次 297 if (unlikely(answer == NULL)) 3. 334 *获取sock,为其分配内存,并根据上面查找到的prot,设置其对应的 335 *成员,并初始化内存。 338 sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1); 4. 344 /*获取端口重用信息*/ 345 if (INET_PROTOSW_REUSE & answer_flags) 346 sk->sk_reuse = 1; 5. 364 *初始化接受与发送队列相关的信息,传输控制块 366 sock_init_data(sock, sk);在这里应当注意,这个是重点看,见notes 6. 392 * 在sk_alloc()里面初始化,作为tcp这里则调用tcp_v4_init_sock() 这里面有个很重要的初始化,在三次握手之中会用到。 即1985行的初始化。 395 if (sk->sk_prot->init) { 396 /* 397 * 设置与tcp特性有关的信息,拥塞控制信息,窗口信息以及 398 * 设置套接字的状态为TCP_CLOSE,这里调用tcp_v4_init_sock() 401 err = sk->sk_prot->init(sk); ---------------------------->1842 /* NOTE: A lot of things set to zero explicitly by call to 1843 * sk_alloc() so need not be done here. 1845 static int tcp_v4_init_sock(struct sock *sk) 1. 1873 /*设置连接状态信息*/ 1874 sk->sk_state = TCP_CLOSE; 在inet_create之中的截图: 对于sk->sk_backlog_rcv的初始化。 ------> int sock_map_fd(struct socket *sock) 将文件节点与套接字联系起来 1.395 struct file *newfile; 396 int fd = sock_alloc_fd(&newfile;); 399 int err = sock_attach_fd(sock, newfile);

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值