Zpět na domů

Diagnostika zpoždění HTTP: od Nginx po jádro

Článek popisuje systémový přístup k diagnostice zpoždění v pipeline zpracování HTTP požadavků. Jsou zváženy metody lokalizace problémů na straně vyvažovače zátěže, aplikace a síťové trati s použitím logů Nginx, hlaviček Server-Timing a metrik jádra Linux TCP_INFO.

Hledání příčin latence v HTTP pipeline za 5 minut
Advertisement 728x90

# Diagnostika zpoždění HTTP požadavků: od vyvažovače zátěže po jádro Linuxu

Rost odpovědi nebo náhlé chyby 5xx v produkci vyžadují okamžitou a přesnou lokalizaci problému. Místo chaotické kontroly služeb je efektivnější použít systémový přístup, který rozdělí celý pipeline zpracování požadavku na síťový a aplikační segment. Probereme metodu rychlého hledání úzkých míst s využitím standardních logů Nginx, hlaviček Server-Timing a nízkourovňových metrik TCP_INFO.

Výchozí bod: analýza logů vyvažovače zátěže

Architektura typické webové aplikace zahrnuje několik vrstev: uživatel, CDN, vyvažovač zátěže (SLB), aplikace, pool spojení a databázový systém. Každá z nich může přidávat zpoždění, ale výchozím bodem pro diagnostiku by měl vždy být vyvažovač. Právě on je ve středu pipeline a umožňuje okamžitě určit směr hledání.

Pro Nginx jsou klíčové dvě proměnné: $request_time a $upstream_response_time. První zaznamenává celkový čas zpracování požadavku od přijetí prvního bajtu od klienta po odeslání posledního bajtu odpovědi. Druhá měří výhradně čas čekání na odpověď od upstream serveru. Pokud se tyto metriky ještě nesbírají, je nutné rozšířit formát logování:

Google AdInline article slot
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;

Interpretace dat vychází z jednoduchého porovnání. Pokud je $upstream_response_time blízko $request_time, úzké místo je vpravo od vyvažovače: v kódu aplikace, poolu spojení nebo databázi. Pokud je $upstream_response_time v normě a $request_time abnormálně vysoký, problém je lokalizován vlevo: v síťové infrastruktuře, směrování CDN nebo na straně klienta. Je důležité vzít v úvahu, že vkládání $request_time do hlaviček odpovědi přes add_header se pro přesné měření nedoporučuje, protože Nginx vytváří hlavičky před dokončením přenosu těla odpovědi. Spolehlivá data jsou dostupná pouze v access logu po uzavření spojení.

Lokalizace problémů na straně aplikace

Když vyvažovač ukazuje na zpoždění v upstream, je nutné detailizovat vnitřní operace aplikace. Standard W3C Server-Timing umožňuje předat klientovi nebo systému monitoringu přesné měření času provedení jednotlivých fází zpracování požadavku. Hlavička má tvar: Server-Timing: app;dur=120, db;dur=95, pool-wait;dur=18. To poskytuje průhlednost bez zavádění těžkých APM agentů.

Integrace hlavičky do stacku aplikace vyžaduje minimální změny. Příklady implementace pro oblíbené jazyky:

Google AdInline article slot

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):

Google AdInline article slot
app.use((req, res, next) => {
  const start = process.hrtime.bigint();
  res.on('finish', () => {
    const ms = Number(process.hrtime.bigint() - start) / 1e6;
    // už odesláno, ale pro logování:
    console.log(`Server-Timing: app;dur=${ms.toFixed(2)}`);
  });
  // pro hlavičku – měříme do odpovědi:
  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();
});

Pro předávání metrik samotného Nginx přes Server-Timing je nutné použít direktivu map, aby se zabránilo předávání prázdných hodnot při obsluze statiky nebo chyb:

Nginx (upstream timing jako Server-Timing):

      # V http {} bloku nginx.conf:
      map $upstream_header_time $server_timing_upstream {
      "-"     "";
       default "ngx-upstream;dur=$upstream_header_time;desc=\"NgxUpstream\"";
      }

      # V location {} bloku vašeho serveru:
      add_header Server-Timing-Ngx "ngx-total;dur=$request_time;desc=\"NgxTotal\"" always;
      add_header Server-Timing-Ngx $server_timing_upstream always;

Všimněte si použití předpony Server-Timing-Ngx a proměnné $upstream_header_time. Prohlížeče očekávají hodnoty v milisekundách, zatímco Nginx vrací sekundy. Navíc $upstream_header_time zaznamenává okamžik přijetí hlaviček od upstream, což přesně odráží čas generování odpovědi, na rozdíl od $upstream_response_time, která se může vynulovat za určitých podmínek buferizace.

Analýza získaných dat umožňuje rychle klasifikovat incident:

  • Dominance db;dur ukazuje na neoptimální SQL dotazy, chybějící indexy nebo blokování tabulek.
  • Vysoké pool-wait;dur signalizuje vyčerpání limitu spojení v PgBounceru nebo podobném pooleru.
  • Velké app;dur při malém db;dur mluví o CPU-vázaných operacích, blokujícím I/O nebo únicích paměti v runtime.
  • Chyby 502/504 na vyvažovači při normálních timingách obvykle znamenají pád procesu aplikace nebo překročení timeoutu na úrovni orchestrátoru.

Síťový segment: fáze HTTP a metriky jádra

Pokud upstream odpovídá rychle a celkové zpoždění roste, problém je v síťové trase. Každý HTTP požadavek prochází šesti po sobě jdoucími fázemi: DNS, Connect (TCP handshake), TLS handshake, Send, Wait (TTFB) a Receive. Anomaly v konkrétních fázích okamžitě zúží okruh hledání. Dlouhý DNS ukazuje na problémy resolveru nebo složité řetězce CNAME. Vysoké hodnoty Connect a TLS svědčí o síťovém zpoždění do edge uzlu CDN, absenci TLS session resumption nebo použití zastaralých protokolů. Pokud jsou Connect a TLS minimální, ale Wait abnormálně vysoké, zpoždění vzniká na úrovni WAF, CDN workerů nebo při fetch požadavku k origin.

Pro hlubokou diagnostiku síťových problémů nestačí standardní HTTP breakdown. Je nutné obrátit se k metrikám jádra Linux přes getsockopt(TCP_INFO). Jádro sleduje každé TCP spojení a poskytuje přesná data: RTT, počet retransmitů, velikost congestion window (cwnd) a RTO. Tyto ukazatele jsou kriticky důležité, protože HTTP timingy nedokážou oddělit síťové zpoždění od času zpracování serveru, když odpověď vejde do jednoho TCP segmentu. Odečtením RTT jádra z fáze Wait technik získá reálný čas zpracování požadavku. Retransmity a stlačený cwnd přímo ukazují na ztrátu paketů a práci algoritmů kontroly přetížení, které přidávají stovky milisekund k každému požadavku. Kombinace aplikačních hlaviček a nízkourovňových síťových metrik vytváří úplný obrázek observability bez závislosti na externích vendorech.

Co je důležité

  • Porovnání $request_time a $upstream_response_time v Nginx okamžitě rozdělí zodpovědnost mezi sítí a aplikací.
  • Hlavička Server-Timing zajišťuje granulární viditelnost vnitřních zpoždění (DB, pooly, runtime) bez zavádění těžkých APM systémů.
  • Analýza fází HTTP požadavku umožňuje přesně identifikovat problémy DNS, TLS handshaků a směrování CDN.
  • Metriky TCP_INFO z jádra Linux poskytují objektivní obrázek síťových ztrát a zpoždění, které nelze zachytit na aplikační úrovni.
  • Systémový přístup k diagnostice latence zkracuje MTTR a vylučuje chaotické restartování služeb během incidentů.

— Editorial Team

Advertisement 728x90

Číst dál