## HTTP-Anfrageslatenz diagnostizieren: Vom Load Balancer zum Linux-Kernel
Ansteigende Reaktionszeiten oder plötzliche 5xx-Fehler in der Produktion erfordern eine sofortige und präzise Problemlokalisierung. Statt Services planlos zu prüfen, ist ein systematischer Ansatz effektiver, der die gesamte Pipeline der Anfrageverarbeitung in Netzwerk- und Anwendungssegmente aufteilt. Wir stellen eine Methode vor, um Engpässe schnell zu identifizieren – mit standardmäßigen Nginx-Logs, Server-Timing-Headern und niedrigstufigen TCP_INFO-Metriken.
Ausgangspunkt: Analyse der Load-Balancer-Logs
Eine typische Web-App-Architektur umfasst mehrere Ebenen: Nutzer, CDN, Load Balancer (SLB), Anwendung, Connection Pool und Datenbank. Jede kann Latenz einführen, doch der Load Balancer sollte immer der Startpunkt für Diagnosen sein – er steht im Zentrum der Pipeline und ermöglicht eine sofortige Richtungsbestimmung bei der Suche.
Bei Nginx sind die Schlüsseldaten $request_time und $upstream_response_time. Die erste erfasst die gesamte Anfrageverarbeitungszeit vom Empfang des ersten Bytes des Clients bis zum Versand des letzten Bytes der Antwort. Die zweite misst nur die Wartezeit auf eine Antwort vom Upstream-Server. Falls diese Metriken noch nicht gesammelt werden, erweitern Sie Ihr Logging-Format:
log_format timing '$remote_addr - $request_uri '
'status=$status '
'rt=$request_time '
'uct=$upstream_connect_time '
'urt=$upstream_response_time';
access_log /var/log/nginx/access.log timing;
Die Dateninterpretation basiert auf einem einfachen Vergleich. Liegt $upstream_response_time nahe bei $request_time, liegt der Engpass rechts vom Load Balancer: im App-Code, Connection Pool oder in der Datenbank. Ist $upstream_response_time normal, aber $request_time auffällig hoch, liegt das Problem links: in der Netzwerkinfrastruktur, CDN-Routing oder auf Client-Seite. Beachten Sie, dass das Einbetten von $request_time in Response-Header via add_header für genaue Messungen nicht empfehlenswert ist, da Nginx Header vor Abschluss der Response-Body-Übertragung formt. Zuverlässige Daten finden sich erst im Access-Log nach Verbindungsende.
App-Seitige Probleme eingrenzen
Wenn der Load Balancer auf Upstream-Verzögerungen hinweist, muss tiefer in die internen Abläufe der App eingedrungen werden. Der W3C-Server-Timing-Standard ermöglicht es, präzise Zeitmessungen für einzelne Anfrageverarbeitungsphasen an Client oder Monitoring-System zu senden. Der Header sieht so aus: Server-Timing: app;dur=120, db;dur=95, pool-wait;dur=18. Das schafft Transparenz ohne schwere APM-Agenten.
Die Integration des Headers in Ihren App-Stack erfordert minimale Änderungen. Implementierungsbeispiele für gängige Sprachen:
Go (net/http):
start := time.Now()
rows, err := db.QueryContext(ctx, query)
dbDur := time.Since(start)
w.Header().Set("Server-Timing",
fmt.Sprintf("db;dur=%.2f", float64(dbDur.Microseconds())/1000))
Python (Django middleware):
class ServerTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start = time.monotonic()
response = self.get_response(request)
dur = (time.monotonic() - start) * 1000
response["Server-Timing"] = f"app;dur={dur:.2f}"
return response
Node.js (Express):
app.use((req, res, next) => {
const start = process.hrtime.bigint();
res.on('finish', () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
// already sent, but for logging:
console.log(`Server-Timing: app;dur=${ms.toFixed(2)}`);
});
// for header — time up to response:
const origEnd = res.end;
res.end = function(...args) {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
res.setHeader('Server-Timing', `app;dur=${ms.toFixed(2)}`);
origEnd.apply(this, args);
};
next();
});
Um Nginx-eigene Metriken via Server-Timing weiterzuleiten, nutzen Sie die map-Direktive, um leere Werte bei statischen Dateien oder Fehlern zu vermeiden:
Nginx (upstream timing as Server-Timing):
# In http {} block of nginx.conf:
map $upstream_header_time $server_timing_upstream {
"-" "";
default "ngx-upstream;dur=$upstream_header_time;desc=\"NgxUpstream\"";
}
# In location {} block of your server:
add_header Server-Timing-Ngx "ngx-total;dur=$request_time;desc=\"NgxTotal\" " always;
add_header Server-Timing-Ngx $server_timing_upstream always;
Beachten Sie den Prefix Server-Timing-Ngx und die Variable $upstream_header_time. Browser erwarten Millisekunden, Nginx liefert Sekunden. Zudem erfasst $upstream_header_time den Moment des Empfangs der Header vom Upstream und spiegelt somit genau die Response-Generierungszeit wider – im Gegensatz zu $upstream_response_time, die unter bestimmten Buffering-Bedingungen auf Null fallen kann.
Die Analyse der Daten ermöglicht eine schnelle Klassifikation des Vorfalls:
- Dominante
db;durweisen auf inoptimale SQL-Queries, fehlende Indizes oder Tabellensperren hin. - Hohe
pool-wait;dursignalisieren erschöpfte Connection-Limits in PgBouncer oder ähnlichen Poolern. - Große
app;durbei kleinerdb;durdeuten auf CPU-gebundene Operationen, blockierende I/O oder Laufzeit-Speicherlecks hin. - 502/504-Fehler am Load Balancer bei normalen Timings bedeuten meist App-Prozess-Abstürze oder Orchestrator-Timeouts.
Netzwerksegment: HTTP-Phasen und Kernel-Metriken
Wenn Upstream schnell antwortet, die Gesamtlatenz aber zunimmt, liegt das Problem im Netzwerkpfad. Jede HTTP-Anfrage durchläuft sechs aufeinanderfolgende Phasen: DNS, Connect (TCP-Handshake), TLS-Handshake, Send, Wait (TTFB) und Receive. Anomalien in bestimmten Phasen grenzen die Suche sofort ein. Lange DNS-Zeiten deuten auf Resolver-Probleme oder komplexe CNAME-Ketten hin. Hohe Connect- und TLS-Werte signalisieren Netzwerklatenz zum CDN-Edge-Node, fehlendes TLS-Session-Resuming oder veraltete Protokolle. Sind Connect und TLS minimal, Wait aber auffällig hoch, liegt die Verzögerung bei WAF, CDN-Workern oder Origin-Fetch.
Für tiefe Netzwerkdiagnosen reichen Standard-HTTP-Aufschlüsselungen nicht aus. Sie benötigen Linux-Kernel-Metriken via getsockopt(TCP_INFO). Der Kernel trackt jede TCP-Verbindung und liefert präzise Daten: RTT, Retransmit-Anzahl, Congestion-Window-Größe (cwnd) und RTO. Diese sind entscheidend, da HTTP-Timings Netzwerklatenz und Server-Verarbeitungszeit nicht trennen können, wenn die Response in ein TCP-Segment passt. Subtraktion des Kernel-RTT aus der Wait-Phase ergibt die echte Anfrageverarbeitungszeit. Retransmits und eingeschränkte cwnd weisen direkt auf Paketverluste und Congestion-Control-Algorithmen hin, die Hunderte Millisekunden pro Anfrage addieren. Die Kombination aus App-Headern und niedrigstufigen Netzwerkmetriken schafft volle Observability ohne Drittanbieter.
Wichtige Erkenntnisse
- Der Vergleich von
$request_timeund$upstream_response_timein Nginx trennt sofort Netzwerk- und App-Verantwortlichkeiten. - Server-Timing-Header bieten granulare Sicht auf interne Verzögerungen (DB, Pools, Runtime) ohne schwere APM-Systeme.
- Die Analyse von HTTP-Anfragephasen identifiziert präzise DNS-, TLS-Handshake- und CDN-Routing-Probleme.
- TCP_INFO-Kernel-Metriken geben einen objektiven Blick auf Netzwerkverluste und -verzögerungen, die auf App-Ebene unsichtbar sind.
- Ein systematischer Diagnoseansatz verkürzt die MTTR und vermeidet chaotische Service-Neustarts bei Vorfällen.
— Editorial Team
Noch keine Kommentare.