Scale · 01 · Connessioni
Il livello connessione è dove finiscono la maggior parte degli incidenti “Redis è lento”. Teoria nel modulo 13; qui si esegue.
Prerequisito: istanza di lab su 6379 (setup).
Esercizio 1.1 — Leggere i limiti reali
Sezione intitolata “Esercizio 1.1 — Leggere i limiti reali”Obiettivo: distinguere il valore configurato dal limite effettivo.
redis-cli CONFIG GET maxclientsredis-cli CONFIG GET timeout tcp-keepalive tcp-backlog io-threadspid=$(pgrep -o redis-server)grep 'open files' /proc/$pid/limitsls /proc/$pid/fd | wc -lOutput atteso (default su Ubuntu/RHEL):
maxclients 10000tcp-backlog 511tcp-keepalive 300timeout 0io-threads 1Verifica: Max open files del processo deve essere ≥ maxclients + 32.
Se è inferiore, Redis abbassa maxclients silenziosamente all’avvio e lo
scrive nel log:
grep -i maxclients ~/redis-lab/single/r.logEsercizio 1.2 — Saturare maxclients e vederlo nei contatori
Sezione intitolata “Esercizio 1.2 — Saturare maxclients e vederlo nei contatori”Obiettivo: produrre rejected_connections > 0 in modo controllato e sapere
dove si legge.
mkdir -p ~/redis-lab/mcredis-server --port 6390 --dir ~/redis-lab/mc --daemonize yes \ --logfile r.log --maxclients 12sleep 1redis-cli -p 6390 CONFIG GET maxclientsApri più connessioni bloccanti del consentito:
for i in $(seq 1 20); do redis-cli -p 6390 BLPOP nolist 6 >/dev/null 2>&1 & donesleep 2redis-cli -p 6390 INFO clients | grep -E 'connected_clients|blocked_clients'redis-cli -p 6390 INFO stats | grep rejected_connectionsOutput atteso (misurato):
maxclients:12rejected_connections:10Verifica: rejected_connections è un contatore cumulativo dall’avvio,
non un gauge. In produzione ciò che conta è la sua derivata: se cresce, i
client stanno sbattendo contro il limite adesso. Mettilo in allerta come
rate(redis_rejected_connections_total[5m]) > 0.
Teardown:
redis-cli -p 6390 SHUTDOWN NOSAVE 2>/dev/null; rm -rf ~/redis-lab/mcEsercizio 1.3 — Identificare chi consuma le connessioni
Sezione intitolata “Esercizio 1.3 — Identificare chi consuma le connessioni”Obiettivo: dalla connessione risalire al client, e chiuderlo in modo mirato.
redis-cli CLIENT LISTredis-cli CLIENT INFOredis-cli CLIENT LIST TYPE normalAggregazione per IP sorgente — il comando che userai davvero in incident:
redis-cli CLIENT LIST \| awk '{for(i=1;i<=NF;i++) if($i ~ /^addr=/){split($i,a,"="); split(a[2],b,":"); print b[1]}}' \| sort | uniq -c | sort -rn | headI client con output buffer che cresce (omem) sono i candidati numero uno:
redis-cli CLIENT LIST \| awk '{for(i=1;i<=NF;i++) if($i ~ /^(id|addr|age|idle|omem|qbuf|cmd)=/) printf "%s ",$i; print ""}' \| sort -t= -k5 -rn | head -5Chiusura mirata e pausa controllata:
redis-cli CLIENT KILL ID <id>redis-cli CLIENT KILL ADDR 127.0.0.1:12345redis-cli CLIENT PAUSE 3000 WRITE # blocca le sole scritture per 3 sredis-cli CLIENT UNPAUSEVerifica: CLIENT KILL restituisce il numero di connessioni chiuse. Con
TYPE normal SKIPME yes chiudi tutti i client applicativi tranne te stesso —
comando da incidente, non da manutenzione ordinaria.
Esercizio 1.4 — Quantificare il costo del round trip
Sezione intitolata “Esercizio 1.4 — Quantificare il costo del round trip”Obiettivo: dimostrare con numeri che la latenza percepita è rete, non server.
redis-cli --intrinsic-latency 3 # solo host, nessuna reteredis-cli --latency # RTT client→server (Ctrl-C per uscire)redis-benchmark -n 20000 -c 10 -t set -qredis-benchmark -n 20000 -c 10 -t set -P 16 -qOutput misurato in loopback su un host di lab:
SET: 80000.00 requests per second, p50=0.079 msec # senza pipeliningSET: 249999.98 requests per second, p50=0.295 msec # con -P 16Verifica: ~3× di throughput con lo stesso server e lo stesso carico. Il p50
per richiesta sale (la pipeline aggrega), il tempo totale crolla. È il dato
da mostrare quando un AM sostiene che “Redis è lento”: se
--intrinsic-latency è basso e --latency è alto, il collo di bottiglia è
rete o client.
Esercizio 1.5 — Socket UNIX e confronto
Sezione intitolata “Esercizio 1.5 — Socket UNIX e confronto”Obiettivo: misurare quanto costa lo stack TCP quando client e server sono sullo stesso host.
redis-cli -s ~/redis-lab/single/redis.sock PINGredis-benchmark -s ~/redis-lab/single/redis.sock -n 20000 -c 10 -t set -qredis-benchmark -n 20000 -c 10 -t set -q # TCP, per confrontoVerifica: il socket UNIX è tipicamente più veloce del loopback TCP. Utile solo in colocation (sidecar, agent sullo stesso nodo): irrilevante se il client è su un altro host.
Esercizio 1.6 — Timeout, keepalive e connessioni zombie
Sezione intitolata “Esercizio 1.6 — Timeout, keepalive e connessioni zombie”Obiettivo: capire l’interazione tra timeout server-side e pool client.
redis-cli CONFIG SET timeout 5redis-cli -p 6379 --no-raw CLIENT INFO | grep -o 'age=[0-9]*'# apri una connessione e lasciala idle(redis-cli -p 6379 --timeout 30 SUBSCRIBE canale &) ; sleep 8redis-cli CLIENT LIST | wc -lredis-cli CONFIG SET timeout 0Verifica: una connessione in SUBSCRIBE non è idle (riceve push), quindi
timeout non la chiude. Le connessioni di un pool applicativo invece sì: un
timeout aggressivo contro un pool persistente produce errori al primo
riutilizzo. Regola: timeout 0 + tcp-keepalive 300, e il keepalive più corto
del timer idle del firewall.
Domande di verifica
Sezione intitolata “Domande di verifica”maxclientsè 10000 maINFO clientsmostramaxclients:4064. Dove guardi per capire perché?rejected_connectionsè a 15000 ma il servizio funziona. È un incidente?- Il p50 per richiesta peggiora con
-P 16. Perché non è un problema? - Un client ha
omem=250mb. Cosa sta succedendo e quale parametro lo limiterà?
Prossimo passo: 02 · Persistenza.