本文介紹當客戶端通過短連接訪問云數據庫 Tair(兼容 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的服務均不適用。