本文介绍当客户端通过短连接访问Redis实例时,报错Cannot assign requested address
的解决方案。
问题原因
该报错通常出现在客户端使用PHP-FPM与PhpRedis组合的架构中,这种架构在高并发场景时,处于TIME-WAIT状态下的TCP连接数较多,客户端无法分配出新的端口,则会出现Cannot assign requested address
报错。
解决方案
使用Pconnect替换Connect(推荐)
用长连接替代短连接,该方案可减少TCP连接,同时可以避免每次请求都会重新建立连接的问题,减少延时。
例如Connect连接代码示例如下:
$redis->connect('[$Hostname]', [$Port]);
$redis->auth('[$Inst_Password]');
参数说明:[$Hostname]、[$Port]和[$Inst_Password]分别为Redis实例的连接地址、端口号和密码,如何查看请参见查看连接地址。
使用Pconnect替换Connect,即使用Persistent Connection的方式连接,示例如下:
$redis->pconnect('[$Hostname]', [$Port], 0, NULL, 0, 0, ['auth' => ['[$Inst_Password]']]);
// 若PhpRedis版本大于等于5.3.0,建议使用Pconnect初始化方式,避免断连时出现no auth。
// timeout、persistent_id、retry_interval和read_timeout等参数根据业务实现情况修改。
修改客户端所在ECS实例的tcp_max_tw_buckets内核参数
对于一些特定场景,例如业务代码牵涉过多组件不易变更等,您可以使用此方案,快速实现高可用。
此方案将直接修改tcp_max_tw_buckets参数,但如果服务端因为重传对应五元组仍然处于LAST-ACK状态时,建立连接会失败。因此,更推荐您使用Pconnect连接方式的方案。
登录客户端所在ECS实例。
执行以下命令,查看ip_local_port_range和tcp_max_tw_buckets参数。
sysctl net.ipv4.tcp_max_tw_buckets net.ipv4.ip_local_port_range
预计返回示例如下。
net.ipv4.tcp_max_tw_buckets = 262144 net.ipv4.ip_local_port_range = 32768 61000
执行以下命令,修改tcp_max_tw_buckets参数,确保tcp_max_tw_buckets的值比ip_local_port_range范围的起始值小。
例如本示例中,ipv4.ip_local_port_range的范围是32768~61000,需修改tcp_max_tw_buckets的值小于32768,示例如下:
sysctl -w net.ipv4.tcp_max_tw_buckets=10000
注意事项
由于tcp_tw_recycle已在Linux 4.12上被弃用,请忽略所有修改tcp_tw_recycle和tcp_tw_reuse的方案,这些方案对于使用了NAT或LVS的服务均不适用。