ChannelOption.SO_BACKLOG的作用
在使用 netty 开发网络应用时,通常会设置 ChannelOption.SO_BACKLOG
,不知你有没想过这个参数的作用是什么。
源码分析
一个常见的配置方式如下所示,通过 ServerBootstrap
的 option()
方法配置 ChannelOption.SO_BACKLOG
的值
1 | ServerBootstrap b = new ServerBootstrap(); |
要搞清楚它的作用,我们可以跟踪找到它的应用位置。一个简单的方式是 debug,跟着 ServerBootstrap
的 bind
方法一路点进去,最终可以定位到 NioServerSocketChannel
的 doBind(SocketAddress)
方法,它的实现如下
1 | // NioServerSocketChannel#doBind |
可以看到是在 jdk ServerSocketChannel
的 bind
方法中使用的。
查看它的方法签名,一共有两个参数。第一个是 SocketAddress
指定了要绑定的地址,第二个就是 backlog
1 | public abstract ServerSocketChannel bind(SocketAddress local, int backlog) |
bind
方法将 channel 的 socket 绑定到一个本地地址,并且配置 socket 以监听远端的连接请求。它将 socket 和本地地址进行关联。关于 backlog
参数的含义,其注释说明如下:
1 | The {@code backlog} parameter is the maximum number of pending |
简单来说,backlog 用于指定 socket 的 pending connection 的最大数量。当然,它的具体语义与底层实现相关,比如可以实现为强制设置一个默认最大值而忽略 backlog 参数。另外,如果将 backlog 设置为 0 或一个负数,那么系统将会使用默认值。
我们来查看下 jdk 中的实现
1 | // ServerSocketChannelImpl#bind |
从中可以看到,如果 backlog
的值设置为 0 或负数,那么将会使用默认值 50
Net.listen
底层使用的是 listen
系统调用,可通过 man 2 listen
查看 listen 的说明
1 | Creation of socket-based connections requires several operations. First, a socket is created with socket(2). Next, a willingness to |
创建一个基于 socket 的连接需要以下几个步骤:
- 通过 socket 系统调用创建一个 socket
- 通过 listen 系统调用监听连接请求,并且设置请求队列的大小限制。listen 系统调用只适用于 SOCK_STREAM 类型的 socket
backlog 参数定义了待接受连接的最大队列长度,如果一个连接到来后发现队列是满的,发起连接请求的 client 将会受到一个
ECONNREFUSED
类型的错误。另外,如果底层协议支持重传,这个请求将被忽略,重传将有可能成功。
与3次握手的关系
tcp 建立连接需要 3 次握手:
- client 发起 SYN 请求:Flags [S.]
- server 返回 ACK,同时带上 SYN 标志:Flags [S.]
- client 返回 ACK:Flags [.]
第二步之后,server 端收到了请求意愿,此时连接还未完全建立,只能算是半连接,还需等待对方的 ACK。
第三步之后,server 端收到了最后一个 ACK,连接已建立。
连接建立后,应用就可以通过 accept 系统调用获取到已建立的连接
1 | accept() extracts the first connection request on the queue of pending connections, creates a new socket |
accept()
从待接受连接队列中获取第一个连接,使用与原 socket 相同的属性创建一个新的 socket,并且为这个 socket 分配对应的文件描述符 FD。如果队列中没有待接受的连接,并且 socket 没有设置非阻塞标记,
accept()
将会阻塞调用者,直到有新的连接出现。如果 socket 标记为非阻塞,并且队列中没有待接受的连接,accept()
将会返回一个错误码
未设置时的默认值是多少
如果不手动设置,netty 会根据系统情况设置一个默认值
1 | // DefaultServerSocketChannelConfig#backlog |
该默认值来自于当前系统下的 somaxconn 取值,相关代码如下:
1 | // Determine the default somaxconn (server socket backlog) value of the platform. |
- 尝试读取 /proc/sys/net/core/somaxconn 文件中的值(适用于Linux),若文件不存在则继续下一步
- 尝试从 sysctl 命令中获取(针对kqueue,适用于BSD或MacOS),前提是设置了
io.netty.net.somaxconn.trySysctl
属性 - 前两步都失败,则使用默认值。windows 下的默认值是 200,其他系统下的默认值是 128
结论
backlog 参数用于设置待接受连接队列的长度,该队列用于放置 3 次握手后建立的连接,应用通过 accept 系统调用从该队列获取已建立的连接,用于后续的处理。
如果你对待接受连接队列的信息感兴趣,可以了解下这篇文章的实验和分析:backlog参数对TCP连接建立的影响
- 2021-09-08
通过 netstat 命令查看网络状态,其中有一列展示的是 socket 的状态,熟练掌握这些状态的含义有助于连接状态的分析与问题排查。
- 2021-04-13
本文记录 java BIO 和 NIO 的使用方式,以方便查阅。
- 2021-02-26
对于一个 Netty client 来说,在配置好
Bootstrap
之后,通过调用其connect
方法来连接到远程服务端,如下所示1
2
3
4Bootstrap b = new Bootstrap();
b.group(group)
...
ChannelFuture f = b.connect(HOST, PORT).sync(); - 2021-04-14
在高性能网络通信框架中,I/O 模型和线程模型是两个核心的关注点。对于 I/O 多路复用模型,需要将 selector 上的事件分发给对应的事件处理者。在 Reactor 线程模型中,Reactor 作为事件分发器,负责分发各种 I/O 事件。
- 2021-02-03
netty 使用
DefaultPromise
完成异步操作,它对 jdk 的 Future 进行了扩展,提供了更丰富的功能。