Diagnostique e resolva o erro Cannot assign requested address ao conectar-se a instâncias do Tair (Redis OSS-compatible) por meio de conexões de curta duração.
Solução rápida
Use conexões persistentes em vez de conexões de curta duração. Em aplicações PHP com phpredis, substitua connect() por pconnect():
// Replace this:
$redis->connect($host, $port);
// With this:
$redis->pconnect($host, $port, 0, null, 0, 0, ['auth' => [$password]]);
Visão geral do problema
|
Aspecto |
Detalhes |
|
Mensagem de erro |
|
|
Cenários afetados |
Alta concorrência com conexões de curta duração |
|
Causa raiz |
Esgotamento de portas TCP devido a conexões em TIME_WAIT |
|
Solução recomendada |
Uso de conexões persistentes ou pool de conexões |
Pré-requisitos
Verifique os seguintes itens:
Acesso à instância ECS cliente que executa sua aplicação
Privilégios de root ou sudo para modificar parâmetros do kernel
Conhecimento sobre a biblioteca cliente Redis da sua aplicação
Sintomas
Esse erro ocorre quando:
A aplicação cria uma nova conexão por requisição
O tráfego apresenta alta concorrência
Existem muitas conexões TCP no estado TIME_WAIT na máquina cliente
Verifique as conexões em TIME_WAIT:
ss -tan state time-wait | wc -l
Se a contagem se aproximar do intervalo de portas disponíveis (28.232 portas por padrão), há esgotamento de portas.
Causa
Nas conexões de curta duração, cada requisição estabelece uma nova conexão TCP. Após o fechamento, o socket entra no estado TIME_WAIT por cerca de 60 segundos antes que a porta fique disponível novamente.
Sob alta concorrência, as conexões abrem e fecham mais rápido do que as portas são recicladas. Isso esgota as portas disponíveis e aciona o erro Cannot assign requested address.
Soluções
Solução 1: Use conexões persistentes (Recomendado)
Conexões persistentes ou pools de conexões reutilizam conexões TCP entre requisições e eliminam o esgotamento de portas.
Benefícios:
Elimina o acúmulo de conexões em TIME_WAIT
Reduz a latência de conexão
Diminui a sobrecarga de CPU dos handshakes TCP
PHP com phpredis
<?php
// Short-lived connection (causes the error)
$redis = new Redis();
$redis->connect($host, $port);
$redis->auth($password);
// Persistent connection (recommended)
$redis = new Redis();
$redis->pconnect($host, $port, 0, null, 0, 0, ['auth' => [$password]]);
Parâmetros para pconnect:
|
Parâmetro |
Descrição |
Valor recomendado |
|
host |
Endpoint da instância Tair |
Endpoint da sua instância |
|
port |
Porta da instância Tair |
6379 (padrão) |
|
timeout |
Tempo limite de conexão em segundos |
0 (sem tempo limite) |
|
persistent_id |
Identificador da conexão persistente |
null |
|
retry_interval |
Intervalo de nova tentativa em milissegundos |
0 |
|
read_timeout |
Tempo limite de leitura em segundos |
0 |
|
auth |
Opções de autenticação |
|
No phpredis 5.3.0 ou superior, passe a senha no array de opções auth para evitar erros NOAUTH durante a reconexão.
Python com redis-py
import redis
# Connection pool (recommended)
pool = redis.ConnectionPool(
host='your-tair-endpoint',
port=6379,
password='your-password',
max_connections=10,
decode_responses=True
)
# Reuse the pool across requests
r = redis.Redis(connection_pool=pool)
r.set('key', 'value')
Node.js com ioredis
const Redis = require('ioredis');
// Single persistent connection (recommended)
const redis = new Redis({
host: 'your-tair-endpoint',
port: 6379,
password: 'your-password',
lazyConnect: true,
keepAlive: 10000
});
// For multiple connections, use a cluster or pool
Solução 2: Ajuste os parâmetros do kernel TCP
Se não for viável modificar o código da aplicação, ajuste o parâmetro de kernel tcp_max_tw_buckets para limitar as conexões em TIME_WAIT. Essa abordagem oferece uma correção rápida, mas é menos eficaz que o uso de conexões persistentes.
Procedimento:
Faça login na instância ECS que executa sua aplicação cliente.
-
Verifique as configurações TCP atuais:
sysctl net.ipv4.tcp_max_tw_buckets net.ipv4.ip_local_port_rangeExemplo de saída:
net.ipv4.tcp_max_tw_buckets = 262144 net.ipv4.ip_local_port_range = 32768 60999 -
Defina
tcp_max_tw_bucketscom um valor menor que o início do seu intervalo de portas:sysctl -w net.ipv4.tcp_max_tw_buckets=10000 -
Para tornar a alteração persistente após reinicializações, adicione a linha ao arquivo
/etc/sysctl.conf:net.ipv4.tcp_max_tw_buckets = 10000 -
Aplique as alterações:
sysctl -p
Limitações:
Se o servidor estiver no estado LAST_ACK durante a retransmissão de pacotes, novas conexões com a mesma tupla-5 podem falhar
Trata-se de uma solução alternativa, não de uma correção definitiva
As conexões persistentes continuam sendo a abordagem recomendada
Escolha a solução adequada
|
Cenário |
Solução recomendada |
|
Desenvolvimento de nova aplicação |
Conexões persistentes |
|
Código existente e fácil de modificar |
Conexões persistentes |
|
Base de código legada complexa |
Ajuste de parâmetros TCP |
|
Aplicações em containers |
Conexões persistentes |
|
Necessidade de correção rápida e temporária |
Ajuste de parâmetros TCP |
Melhores práticas
Parâmetros obsoletos
O parâmetro tcp_tw_recycle foi removido no kernel Linux 4.12. Evite usar tcp_tw_recycle e tcp_tw_reuse, especialmente em ambientes com:
Network Address Translation (NAT)
Linux Virtual Server (LVS)
Balanceadores de carga
Kubernetes ou plataformas de orquestração de containers
Recomendações de monitoramento
Monitore estas métricas para evitar a recorrência do problema:
Contagem de conexões TIME_WAIT nas máquinas clientes
Clientes conectados ao Redis (
INFO clients)Erros de conexão nos logs da aplicação
Utilização de portas nas máquinas clientes