均衡转发
多线程服务端编程中经常遇到的一个问题就是,如何将客户端的连接请求均衡地分发给每个线程。一种常见的做法是让多个线程同时调用accept()
,在同一个socket描述符上等待连接请求,当请求到达时,内核会随机唤醒一个线程去处理这个请求。(注:Linux 2.6之后,accept()
不会导致惊群。) 然而,正如这篇文章所说的,当连接请求到达时,若系统负载过高,内核就可能会以一种很不均衡地方式去唤醒线程。这就导致了有的线程处理太多请求而忙不过来,而有的线程则很空闲。
Linux 3.9提供了SO_REUSEPORT
选项,对于TCP来说,它允许多个listening socket绑定到同一个端口,也就是说,所有线程都可以创建一个listening socket绑定到同一个端口,并且可以调用accept()
在这个socket上等待连接请求。使用SO_REUSEPORT
的好处就是,由于内核会将客户端的连接请求均衡地分发给每个listening socket,在多线程环境中,这有利于充分利用好每个 CPU 核心。
SO_REUSEPORT
的用法很简单,只要每个listening socket在调用bind()
之前设置好这个选项就可以了:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
bind(sockfd, ...);