众所周知,SO_REUSEPORT允许多个套接字侦听相同的IP地址和端口组合,它将每秒请求增加2到3倍,并减少延迟(~30%)和延迟标准差(8次):https ://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
NGINX版本1.9.1引入了一项新功能,该功能支持使用 SO_REUSEPORT套接字选项,该选项适用于许多操作系统的较新版本,包括DragonFly BSD和Linux(内核版本3.9及更高版本).此套接字选项允许多个套接字侦听相同的IP地址和端口组合.然后内核负载平衡套接字上的传入连接....
如图所示,reuseport将每秒请求数增加了2到3倍,并减少了延迟和延迟的标准偏差.
SO_REUSEPORT适用于大多数现代操作系统:Linux(自2013年4月29日起内核> = 3.9),Free/Open/NetBSD,MacOS,iOS/watchOS/tvOS,IBM AIX 7.2,Oracle Solaris 11.1,Windows(仅表现为2个标志)在BSD上一起+ ,可能在Android上:https: //stackoverflow.com/a/14388707/1558037SO_REUSEPORTSO_REUSEPORTSO_REUSEADDR
Linux> = 3.9
- 此外,内核为
SO_REUSEPORT其他操作系统中找不到的套接字执行一些"特殊魔法" :对于UDP套接字,它尝试均匀分布数据报,对于TCP侦听套接字,它会尝试分发传入的连接请求 (通过调用接受的请求accept())均匀地跨越共享相同地址和端口组合的所有套接字.因此,应用程序可以轻松地在多个子进程中打开相同的端口,然后使用它SO_REUSEPORT来获得非常便宜的负载平衡.
同样众所周知,为了避免自旋锁定和高性能锁定,不应该有超过1个线程的套接字.即每个线程都应该处理自己的套接字以进行读/写.
accept()对于相同的套接字描述符是线程安全的函数,因此它应该被锁定 - 因此锁争用会降低性能:http://unix.derkeiler.com/Newsgroups/comp.unix.programmer/2007-06/msg00246.htmlPOSIX.1-2001/SUSv3 需要accept(),bind(),connect(),listen(),socket(),send(),recv()等作为线程安全函数.标准中可能存在关于它们与线程交互的一些含糊之处,但其意图是它们在多线程程序中的行为受标准控制.
我坐下来阅读Apache的MPM prefork.c,这段代码使用了一个名为accept_mutex阻止多个线程调用的变量accept().这很奇怪,因为据我所知accept()是线程安全的.
accept()线程安全吗?这是平台兼容性问题吗?如果是这样,哪个平台实现了非线程安全accept()?
我使用的是Linux 3.2.0,x86_64.我可以同时从多个线程调用一个套接字的accept()吗?
在阅读TCP源码时,我发现一个困惑的事情:
我知道 TCP 在 3 次握手中有两个队列:
SYN回的连接ACK + SYN,我们称之为syn 队列。但是在阅读代码时,我发现listen()will call inet_csk_listen_start(),它将调用reqsk_queue_alloc()create icsk_accept_queue。并且该队列用于 中accept(),当我们发现队列不为空时,我们将从它获取连接并返回。
更重要的是,跟踪接收过程后,调用堆栈是这样的
tcp_v4_rcv()->tcp_v4_do_rcv()->tcp_rcv_state_process()
Run Code Online (Sandbox Code Playgroud)
收到第一次握手时,服务器状态为 LISTEN。所以它会调用
`tcp_v4_conn_request()->tcp_conn_request()`
Run Code Online (Sandbox Code Playgroud)
在tcp_conn_request()
if (!want_cookie)
// Add the req into the queue
inet_csk_reqsk_queue_hash_add(sk, req, tcp_timeout_init((struct sock *)req));
Run Code Online (Sandbox Code Playgroud)
但这里的队列正是icsk_accept_queue,而不是 syn 队列。
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
unsigned long timeout)
{
reqsk_queue_hash_req(req, timeout);
inet_csk_reqsk_queue_added(sk);
}
static inline void inet_csk_reqsk_queue_added(struct sock *sk)
{ …Run Code Online (Sandbox Code Playgroud)