菜单
Fatal Error: Cannot assign requested address

问题

在执行一个PHP cli 常驻进程程序时,报致命错误:Cannot assign requested address,打开调试模式,具体错误可看到是 redis 建立连接语句导致。

这个错误 "Cannot assign requested address" 是由于 linux 分配的客户端连接端口用尽,客户端无法分配出新的端口,则会出现该问题。并发量较大的情况下,处于 TIME_WAIT状态下的TCP连接数较多无法建立新的连接所致。

虽然连接可以正常关闭,但是端口不是立即释放,而是处于 TIME_WAIT 状态,默认等待 60s 后才释放。

分析

该应用程序是会持续消耗 redis 队列消息,本身是使用了 supervisor 进行多进程执行。手动启用调试时报的错误,所以直接查看 redis 的连接看看

# 查看系统连接概览(注意观察 timewait 后面数字)
ss -s

# 查看redis端口分配情况
netstat -apn | grep :6379 | wc -l

统计了下,有22w个处于 TIME_WAIT 状态的。

处理

经过查看程序中 redis 默认使用了 connect 短连接方式,更换为 pconnect 持久连接即可。而且处理完成后系统CPU消耗由49%左右下降为14%左右,负载也明显降低。

<br />

顺便调整下系统参数

# 查看系统参数
sysctl net.ipv4.tcp_max_tw_buckets \
       net.ipv4.ip_local_port_range \
       net.ipv4.tcp_fin_timeout  

我的机器 4C8G 输出如下信息

# 系统 tcp 最大 time wait 状态的允许保持数量
net.ipv4.tcp_max_tw_buckets = 262144
# 系统本地端口号范围
net.ipv4.ip_local_port_range = 32768  61000
# 系统 tcp 回收超时时间(秒)
net.ipv4.tcp_fin_timeout = 60

临时修改

sysctl -w net.ipv4.tcp_max_tw_buckets=10000
sysctl -w net.ipv4.tcp_fin_timeout=20

我这里把 tcp 超时由 60 秒修改为了 20 秒,如若修改tcp_max_tw_buckets参数,确保tcp_max_tw_buckets的值比ip_local_port_range范围的值小。

系统级参数请务必小心修改!

永久修改:编辑配置文件 /etc/sysctl.conf,并找到对应参数修改即可。

# 使配置文件立即生效
sysctl -p

参考文章