「我正在参与掘金会员专属活动-源码共读第一期」
跟着挖坑的张师傅开启源码阅读之旅。
Netty 服务端启动服务流程
学习任务一
backlog 参数有什么作用,请仔细研究这个重要参数。
打断点 ServerSocketChannelImpl # bind
Net.listen方法的入参使用到了backlog 参数的值
跳转到Net类,发现listen 方法是native修饰的
在openjdk 源码(openjdk8u/src/windows/native/sun/nio/ch)中找到Net.c
查看具体代码执行的是系统的winsock2.h中的listen方法
查看winsock2.h中listen方法的定义,重点查看backlog参数的含义
截图
backlog 【积压】
这里作为参数的含义是等待连接的队列的最大长度。
Linux系统listen方法中backlog参数的含义
下面内容摘抄自 juejin.cn/post/684490…
什么是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。
经过以上三个过程,client 端和 server 端建立了连接,即所谓三次握手的过程。
由此,我们已经知道了什么是backlog,以及半连接队列、全连接队列的由来。
backlog 参数的取值
通过调试知道调用ServerSocketChannelImpl#bind方法的是 ServerSocketAdaptor#bind方法
从上图源码中可知,backlog的取值为50
backlog设置标准
在设置backlog时,既不能太大,也不能太小,设置太大,当访问流量突然增加超过服务器的负载时,客户端不能快速失败,造成读取连接超时,对服务端来讲,会影响网络I/O,同时造成内存使用过大,cpu负载增加;如果设置太小,不能充分发挥服务端的负载能力,并且会客户端造成连接失败的情况。应根服务端能够承受的最大qps进行设置,backlog的大小,应设置成在服务端最大能够承受qps的1-1.5倍左右。