用虚拟IP扩容端口池:解决高并发WebSocket端口耗尽问题

问题背景

最近遇到一个棘手的问题:

服务器的WebSocket服务在高并发场景下,客户端连接数达到一万多时,服务器的端口资源被耗尽了。导致后面的任何请求都没响应。

通过 netstat 查看连接情况,发现所有连接的本地端口都是从32768开始的,即使我已经将系统的端口范围设置为1024-65535:

sh 复制代码
[root@server ~]# cat /proc/sys/net/ipv4/ip_local_port_range
1024    65535

但实际分配的端口还是从32768开始,导致高位端口很快被耗尽,新连接无法建立。

问题分析

经过一番调研,发现这个问题在Linux系统中很常见。虽然设置了 ip_local_port_range 为1024-65535,但实际上:

  1. glibc的默认行为:很多Linux发行版的glibc硬编码了临时端口分配的起始值为32768

  2. 内核版本限制:老版本内核(如3.10)没有 ip_unprivileged_port_start 参数

  3. 历史遗留问题:这是Linux系统的"正常"行为,不是配置错误

我的服务器环境:

  • 内核:3.10.0-1127.19.1.el7.x86_64

  • glibc:2.17

  • 系统:CentOS 7

解决方案探索

方案一:升级系统(不推荐)

升级glibc和内核到新版本可以支持更灵活的端口分配,但在生产环境中风险极高,容易导致系统崩溃。

方案二:虚拟IP扩容(推荐)

通过在同一台服务器上添加多个虚拟IP,每个IP都拥有独立的端口池,从而成倍扩容端口资源。

虚拟IP方案实施

第一步:查看网络环境

首先确认服务器的网卡和IP配置:

sh 复制代码
[root@server ~]# ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:16:3e:1f:ee:09 brd ff:ff:ff:ff:ff:ff
    inet 1.2.3.17/20 brd 11.22.33.255 scope global dynamic eth0

注:这里的ip随便编的

可以看到主IP是 1.2.3.17/20,子网掩码是 /20。

第二步:添加虚拟IP

根据主IP的网段,添加几个虚拟IP:

sh 复制代码
ip addr add 1.2.3.100/20 dev eth0
ip addr add 1.2.3.101/20 dev eth0
ip addr add 1.2.3.102/20 dev eth0

这里的ip后三位可以随便写。ip个数最好是100个以内,多了会影响性能。
在没这样添加这些虚拟ip时,Ping这些虚拟ip是ping不通的,添加后就可以ping通了

验证添加成功:

sh 复制代码
ip addr show eth0

显示出刚才添加的ip就是没问题的

第三步:配置Nginx负载均衡

修改Nginx配置,将流量分发到不同的虚拟IP:

json 复制代码
upstream websocket_backend {
    server 1.2.3.100:38088;
    server 1.2.3.101:38088;
    server 1.2.3.102:38088;
}

server {
    listen 80;
    server_name xx.com;

    location /请求上下文 {
        proxy_pass http://websocket_backend/请求上下文;
        proxy_redirect default;
        proxy_pass_header Server;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
    }
}

第四步:重启服务

重启WebSocket服务端和Nginx,让配置生效:

sh 复制代码
nginx -t
nginx -s reload

效果验证

查看连接分布

使用 ss 命令查看连接是否被正确分发到不同虚拟IP:

sh 复制代码
ss -tnp | grep 38088

输出显示连接确实被分发到了不同IP:

sh 复制代码
CLOSE-WAIT 1      0      1.2.3.100:38088              169.24.13.2:36294
CLOSE-WAIT 1      0      1.2.3.101:38088              169.24.13.2:57430
CLOSE-WAIT 1      0      1.2.3.102:38088              169.24.13.2:37558

这里的169.24.13.2不用管,这是我的nginx用的docker镜像的原因

端口资源扩容效果

  • 之前:所有连接都占用 1.2.3.17 的端口池(32768~65535)

  • 现在:连接分散到3个虚拟IP,每个IP都有独立的端口池

  • 效果:端口资源扩容3倍,极大缓解端口耗尽问题

注意事项

1. 子网掩码的确定

添加虚拟IP时,子网掩码必须和主IP一致。查看方法很简单:

sh 复制代码
ip addr show eth0

看输出中的 inet 1.2.3.17/20,直接使用 /20 即可。

2. 虚拟IP的持久化

临时添加的虚拟IP重启后会失效,需要写入启动脚本:

sh 复制代码
# 编辑 /etc/rc.local
ip addr add 1.2.3.100/20 dev eth0
ip addr add 1.2.3.101/20 dev eth0
ip addr add 1.2.3.102/20 dev eth0

# 赋予执行权限
chmod +x /etc/rc.d/rc.local

3. 网络架构要求

  • Nginx和WebSocket服务端必须在同一台服务器

  • 虚拟IP必须在同一网段内,不能和其他机器冲突

  • 如果是云服务器,可能需要先在控制台申请辅助私网IP

总结

通过虚拟IP + Nginx负载均衡的方案,成功将端口资源扩容了3倍,有效解决了高并发WebSocket场景下的端口耗尽问题。这个方案的优势:

  1. 实施简单:只需添加虚拟IP和配置Nginx

  2. 风险较低:不需要升级系统内核

  3. 效果显著:端口资源成倍扩容

  4. 扩展性好:可以根据需要继续添加虚拟IP

对于高并发WebSocket、短连接等场景,这是一个非常实用的解决方案。

相关推荐
ai小鬼头23 分钟前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
2501_9159214324 分钟前
iOS IPA 混淆实测分析:从逆向视角验证加固效果与防护流程
websocket·网络协议·tcp/ip·http·网络安全·https·udp
2501_9159184124 分钟前
打造可观测的 iOS CICD 流程:调试、追踪与质量保障全记录
websocket·网络协议·tcp/ip·http·网络安全·https·udp
Touper.29 分钟前
SpringBoot -- 自动配置原理
java·spring boot·后端
一只叫煤球的猫1 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
一只鹿鹿鹿1 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
vvilkim1 小时前
Electron 自动更新机制详解:实现无缝应用升级
前端·javascript·electron
vvilkim1 小时前
Electron 应用中的内容安全策略 (CSP) 全面指南
前端·javascript·electron
aha-凯心1 小时前
vben 之 axios 封装
前端·javascript·学习
漫谈网络1 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket