Guida completa a timeout, worker e gestione dei carichi applicativi
In un'infrastruttura dove Apache HTTP Server funge sia da frontend (TLS, routing, bilanciamento) che da tramite verso container Docker, la stabilità non dipende dalla potenza dell'hardware, ma dalla coerenza della catena dei timeout, dai limiti del sistema operativo e dal dimensionamento dell'intero stack applicativo.
Questa guida è focalizzata sul caso reale in cui:
- Apache frontend agisce da reverse proxy e load balancer
- Il backend applicativo gira in container
- In alcuni casi Apache è anche il web server dentro il container
- L’applicazione è PHP (ma i principi restano validi anche per Python o altri stack)
1. Il Sistema Operativo: Preparare il terreno (Systemd)
Prima di configurare Apache, dobbiamo assicurarci che il sistema operativo gli permetta di lavorare.
Sui sistemi moderni come Debian o Ubuntu, il limite di processi e thread per servizio è gestito da systemd tramite i Cgroups.
Il problema
Sotto carico Apache può raggiungere il limite di task e generare errori nei log simili a:
Fork Rejected
La soluzione
Aumentare TasksMax per il servizio Apache.
systemctl edit apache2.service
Inserire:
[Service]
TasksMax=3000
Applicare:
systemctl daemon-reload
systemctl restart apache2
Per un server con 4 GB RAM e ~400 worker, 3000 è un valore equilibrato.
2. Monitoraggio in tempo reale: i comandi “Manometro”
Per capire se il limite è corretto, interroga direttamente i Cgroups:
cat /sys/fs/cgroup/system.slice/apache2.service/pids.current
cat /sys/fs/cgroup/system.slice/apache2.service/pids.max
Se pids.current si avvicina a pids.max, il server inizierà a rifiutare connessioni.
Questo è il primo indicatore reale di saturazione.
3. Il Motore: MPM Event dimensionato per 4GB RAM
Il modulo MPM Event gestisce le connessioni in modo asincrono, evitando che i thread restino bloccati su KeepAlive.
Configurazione:
/etc/apache2/mods-available/mpm_event.conf
<IfModule mpm_event_module>
ServerLimit 16
StartServers 3
ThreadsPerChild 25
MaxRequestWorkers 400
MinSpareThreads 25
MaxSpareThreads 75
MaxConnectionsPerChild 10000
</IfModule>
Perché questi numeri?
- 16 processi × 25 thread = 400 richieste simultanee
- circa 800 MB occupati da Apache
- RAM restante per PHP, sistema e cache
Se PHP cresce, Apache deve ridursi.
4. La Catena dei Timeout: Sincronia Proxy-Backend
Se proxy e backend non sono coordinati, i worker restano occupati inutilmente.
Regola fondamentale: l’applicazione deve morire prima del proxy.
| Parametro | Proxy | Backend | Note |
|---|---|---|---|
| KeepAliveTimeout | 3s | 5s | Il backend aspetta di più |
| Timeout | 60s | 65s | Ricezione dati |
| ProxyTimeout | 300s | – | Attesa risposta backend |
| PHP max_execution_time | – | 290s | L’app muore prima del proxy |
5. Configurazione Globale dei Timeout (operativa)
Creare:
/etc/apache2/conf-available/global-timeouts.conf
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 3
Timeout 60
ProxyTimeout 300
Attivare:
a2enconf global-timeouts
systemctl reload apache2
Questo file evita configurazioni duplicate nei VirtualHost.
6. Bilanciamento e Failover Rapido
Il failover lento è uno dei problemi più comuni nei proxy Apache.
Usiamo connectiontimeout per forzare il rilevamento immediato dei nodi down.
<Proxy "balancer://app_cluster">
BalancerMember "http://node01:8080" route=n1 connectiontimeout=2 retry=30
BalancerMember "http://node02:8080" route=n2 connectiontimeout=2 retry=30
ProxySet lbmethod=bybusyness timeout=60
</Proxy>
Con questo:
- un nodo morto viene escluso in ~2 secondi
- il traffico migra subito sugli altri
7. Separare traffico veloce e lento
Questa tecnica serve a proteggere le richieste normali da quelle pesanti.
È corretta solo se il backend lento ha risorse dedicate.
# Pool veloce
<Proxy "balancer://app_fast">
BalancerMember "http://app:8080" connectiontimeout=2
ProxySet timeout=60
</Proxy>
# Pool lento
<Proxy "balancer://app_slow">
BalancerMember "http://app_dedicated:8080" connectiontimeout=2
ProxySet timeout=1200
</Proxy>
Se entrambi puntano allo stesso backend, non serve a nulla.
8. PHP-FPM: il vero limite della capacità
Apache accetta la richiesta, ma PHP la esegue.
Per 4 GB con 2 GB dedicati a PHP:
pm.max_children ≈ RAM_php / memoria_media_script
Configurazione:
/etc/php/8.x/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 40
pm.start_servers = 10
pm.max_requests = 500
request_terminate_timeout = 300s
Se PHP si satura, Apache sembra lento anche se non lo è.
9. Quando il tuning non basta: strategie architetturali
Il tuning dei timeout migliora la stabilità, ma non cambia il modello di carico.
Le operazioni lunghe dovrebbero diventare asincrone.
Soluzioni tipiche
- code con Redis
- code con RabbitMQ
- job worker separati
- risposta immediata “presa in carico”
Questo libera i worker Apache in pochi millisecondi.
10. Checklist finale operativa
✔ TasksMax aumentato e verificato
✔ MPM Event dimensionato sulla RAM
✔ Timeout coerenti tra proxy, backend e PHP
✔ Failover rapido con connectiontimeout
✔ Backend lento separato solo se realmente isolato
✔ PHP-FPM dimensionato sulla RAM reale
✔ Monitoraggio periodico dei pids Apache
Conclusione
La stabilità di un reverse proxy Apache non dipende da un singolo parametro, ma dall'allineamento di:
- limiti del sistema operativo
- modello di concorrenza di Apache
- timeout della catena proxy-backend
- capacità reale dell’applicazione
Quando questi quattro livelli sono coerenti, Apache diventa estremamente stabile anche sotto carico elevato.
Top comments (0)