Nginx 的端口复用:提升服务器并发能力
在高并发服务器环境中,端口复用是一项非常重要的技术。它可以显著提升服务器的并发处理能力,尤其是在 Nginx 等高性能服务器中。本文将详细介绍 Nginx 中的端口复用技术,特别是 SO_REUSEADDR
和 SO_REUSEPORT
的使用方法和原理。
# 1. 背景
在 TCP 连接关闭后,会进入 TIME_WAIT
状态,持续一段时间(通常是 2 * MSL,最大报文段生存时间),以确保所有数据包都能被正确处理。在 TIME_WAIT
状态期间,端口默认情况下是无法立即被重新使用的。这对于需要频繁重启的服务器进程或高并发连接的服务器来说,是一个性能瓶颈。
# 2. TCP TIME_WAIT
状态
在 TCP 连接关闭的四次挥手(Four-Way Handshake)过程中,主动关闭方在发送最后一个 ACK 报文后会进入 TIME_WAIT
状态。这个状态的存在是为了确保所有数据包都能被正确处理,防止旧连接的数据包影响新连接。
# 3. SO_REUSEADDR
的作用
SO_REUSEADDR
是一个 socket 选项,允许在端口处于 TIME_WAIT
状态时重新使用该端口。通过设置这个选项,可以在端口处于 TIME_WAIT
状态时,允许新的连接请求绑定到这个端口,从而提高服务器的可用性和性能。
# 代码示例
以下是一个在 C 语言中使用 SO_REUSEADDR
的示例代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main() {
int sockfd;
int opt = 1;
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
return 1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}
// listen, accept, etc...
close(sockfd);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 4. SO_REUSEPORT
的作用
SO_REUSEPORT
是另一个 socket 选项,允许多个套接字绑定到同一个端口,从而实现负载均衡和提高并发处理能力。它在高性能服务器(如 Nginx、Redis 等)中广泛使用。
# 代码示例
以下是一个在 C 语言中使用 SO_REUSEPORT
的示例代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main() {
int sockfd;
int opt = 1;
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
return 1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}
// listen, accept, etc...
close(sockfd);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 5. 在 Nginx 中配置 SO_REUSEADDR
和 SO_REUSEPORT
# 确认 Nginx 版本
首先,确保你的 Nginx 版本支持 SO_REUSEPORT
。Nginx 1.9.1 及以上版本支持这个选项。
# 编译 Nginx 以支持 SO_REUSEPORT
如果你的 Nginx 是从源代码编译的,可以在编译时添加支持 SO_REUSEPORT
的模块。具体步骤如下:
./configure --with-stream --with-stream_ssl_module --with-stream_realip_module
make
make install
2
3
# 修改 Nginx 配置文件
在 Nginx 配置文件中,SO_REUSEADDR
默认已启用,为了启用 SO_REUSEPORT
,需要在配置文件中添加相应的指令。
编辑你的 Nginx 配置文件(通常是 /etc/nginx/nginx.conf
或 /usr/local/nginx/conf/nginx.conf
),在相应的 server
块中添加 reuseport
指令。例如:
worker_processes 4;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/conf.d/*.conf;
server {
listen 80 reuseport;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在 listen
指令后添加 reuseport
,即可启用 SO_REUSEPORT
。
# 重启 Nginx
配置修改后,重启 Nginx 使配置生效:
sudo nginx -s reload
# 6. 验证配置
通过以下命令可以查看端口的复用情况:
ss -lntp | grep nginx
如果 SO_REUSEPORT
配置成功,你会看到多个 Nginx 进程都在监听同一个端口。
# 总结
通过配置 SO_REUSEADDR
和 SO_REUSEPORT
,可以显著提升 Nginx 服务器的并发处理能力和性能。SO_REUSEADDR
允许在端口处于 TIME_WAIT
状态时重新使用该端口,而 SO_REUSEPORT
允许多个套接字绑定到同一个端口,实现负载均衡。这些配置在高并发服务器环境中非常有用,能够有效提高服务器的可用性和稳定性。
- 02
- Flink 集群部署指南 原创09-20
- 03
- MongoDB 集群Config Server 复制集的工作原理09-14