Connection Pooling und Keep-Alive fuer weniger Overhead
60fps
ms
Performance · Netzwerk · Connection Pooling · Backend
Connection Pooling und Keep-Alive fuer weniger Overhead
Wie wiederverwendete Verbindungen Handshake-Kosten eliminieren

Jede neue TCP- und TLS-Verbindung kostet mehrere Netzwerk-Umlaufzeiten, bevor das erste Nutzbyte beim Client ankommt, und dieser Aufbau wiederholt sich bei naiven Implementierungen bei jedem Request erneut. HTTP Keep-Alive, Connection Pools zwischen PHP-FPM und Backend-Diensten sowie modernes HTTP/2- und HTTP/3-Multiplexing vermeiden genau diesen Overhead. Dieser Artikel zeigt, wie Nginx, Redis, MySQL und Guzzle-Clients Verbindungen gezielt wiederverwenden.

16 Min. Lesezeit TCP/TLS Handshake · Keep-Alive Connection Pooling · Magento 2

1. TCP- und TLS-Handshake: die versteckten Kosten jeder neuen Verbindung

Bevor ein Browser oder ein Backend-Dienst auch nur ein einziges Anwendungs-Byte über eine neue Verbindung senden kann, muss zuerst der TCP-Drei-Wege-Handshake abgeschlossen werden: Der Client sendet ein SYN-Paket, der Server antwortet mit SYN-ACK, der Client bestätigt mit ACK. Das kostet eine volle Umlaufzeit (Round Trip Time, RTT), bevor überhaupt ein HTTP-Request möglich ist. Bei HTTPS kommt danach der TLS-Handshake obendrauf: Zertifikatsaustausch, Schlüsselnegotiation und die kryptografische Bestätigung, dass beide Seiten denselben Sitzungsschlüssel berechnet haben. Unter TLS 1.2 sind dafür in der Standardkonfiguration zwei weitere Umlaufzeiten nötig, in Summe also drei RTT, bevor der erste Response-Byte beim Client ankommt. Auf einer Mobilverbindung mit 150 bis 200 Millisekunden Latenz zum Server bedeutet das allein 450 bis 600 Millisekunden reinen Verbindungsaufbau, ohne dass auch nur ein Byte Nutzdaten geflossen ist.

TLS 1.3 reduziert diesen Overhead spürbar: Der Client schickt sein Schlüsselmaterial bereits im ersten Client Hello mit, sodass Server Hello, Zertifikat und Finished-Nachricht in einer einzigen Antwort zurückkommen. Das drückt den TLS-Anteil auf eine Umlaufzeit, kombiniert mit dem TCP-Handshake also zwei RTT bis zum ersten Response-Byte. Mit Session-Resumption über Pre-Shared Keys aus einer vorherigen Verbindung ist bei TLS 1.3 sogar 0-RTT für wiederkehrende Verbindungen zum selben Host möglich, wobei der erste Request bereits im initialen Datenpaket mitgeschickt wird. Dieser Unterschied zwischen zwei und drei RTT klingt gering, multipliziert sich aber, sobald eine Seite Dutzende externe Ressourcen von unterschiedlichen Hosts lädt oder ein Backend-Dienst hunderte Male pro Sekunde eine neue Verbindung zu einer API aufbaut, statt eine bestehende wiederzuverwenden.

2. HTTP Keep-Alive: eine offene Verbindung fuer mehrere Requests nutzen

Unter HTTP/1.0 war eine Verbindung standardmäßig für genau einen Request gedacht: Nach der Antwort schloss der Server die TCP-Verbindung, und für den nächsten Request begann der gesamte Handshake-Prozess von vorne. HTTP/1.1 hat das mit persistenten Verbindungen als Standardverhalten geändert. Über den Connection-Header signalisieren Client und Server, dass die Verbindung nach einer Antwort offen bleibt, sodass der nächste Request dieselbe bereits aufgebaute TCP- und TLS-Verbindung nutzen kann. Der Browser lädt so HTML, CSS, JavaScript und Bilder von derselben Domain über eine kleine Anzahl wiederverwendeter Verbindungen, statt für jede Ressource einen neuen Handshake zu bezahlen. Das reduziert nicht nur Latenz, sondern auch die CPU-Last auf Client und Server, weil TLS-Schlüsselnegotiation ein rechenintensiver Vorgang ist, der sich bei jedem neuen Handshake wiederholt.

Keep-Alive ist dabei kein Automatismus ohne Grenzen: Eine offene Verbindung bindet Speicher und einen Worker-Slot auf dem Server, solange sie besteht, auch wenn gerade kein Request läuft. Server müssen deshalb eine Balance finden zwischen möglichst langer Wiederverwendung, um Handshake-Kosten zu sparen, und einem Timeout, der ungenutzte Verbindungen wieder freigibt, damit Ressourcen nicht durch inaktive Clients blockiert werden. Diese Balance wird über konkrete Konfigurationswerte im Webserver gesteuert, die im nächsten Abschnitt im Detail betrachtet werden.

3. Keep-Alive im Webserver konfigurieren: keepalive_timeout und keepalive_requests

Nginx steuert das Verhalten client-seitiger Keep-Alive-Verbindungen über zwei zentrale Direktiven. keepalive_timeout legt fest, wie lange eine Verbindung nach der letzten Antwort im Leerlauf offen bleibt, bevor Nginx sie aktiv schließt, typischerweise 60 bis 75 Sekunden in produktiven Setups. keepalive_requests begrenzt, wie viele Requests über eine einzelne Verbindung verarbeitet werden dürfen, bevor sie zwangsweise neu aufgebaut wird, standardmäßig 1000. Dieser zweite Wert existiert, um Speicherfragmentierung in langlebigen Worker-Prozessen zu vermeiden und die Last gleichmäßiger über verfügbare Worker zu verteilen, wenn eine Seite sehr viele Ressourcen über wenige Verbindungen lädt.

Zusätzlich zur reinen Keep-Alive-Konfiguration lohnt sich die TLS-Session-Resumption: Mit ssl_session_cache und aktivierten Session-Tickets kann ein Client, der eine Verbindung neu aufbaut, einen bereits ausgehandelten Sitzungsschlüssel wiederverwenden, statt den vollständigen TLS-Handshake erneut zu durchlaufen. Das ist besonders wirksam für wiederkehrende Besucher und für Backend-zu-Backend-Verbindungen, die in kurzen Abständen neu verbinden.


# nginx.conf - Keep-Alive tuning for client-facing connections
http {
    keepalive_timeout 65s;      # close idle client connections after 65s
    keepalive_requests 1000;    # allow up to 1000 requests per connection

    server {
        listen 443 ssl http2;
        server_name shop.mironsoft.de;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 1d;
        ssl_session_tickets on;   # enable session resumption, avoid full handshake
    }
}

4. Upstream Keep-Alive: Connection Pools zwischen Reverse Proxy und Backend

Die client-seitige Keep-Alive-Konfiguration betrifft nur die Verbindung zwischen Browser und Nginx. Für Verbindungen, die Nginx selbst als Reverse Proxy zu einem Backend-Dienst aufbaut, etwa zu einer internen Such-API oder einem Microservice, gilt eine eigene Konfiguration. Ohne explizite Einstellung schließt Nginx die Verbindung zum Upstream nach jedem einzelnen Request und baut sie für den nächsten Client-Request komplett neu auf, obwohl derselbe Ziel-Host angesprochen wird. Das verursacht denselben Handshake-Overhead server-seitig, den Keep-Alive client-seitig eigentlich vermeidet.

Die Lösung ist ein Upstream-Keep-Alive-Pool: Die keepalive-Direktive im upstream-Block legt fest, wie viele bereits offene Verbindungen pro Worker-Prozess für die Wiederverwendung vorgehalten werden. Voraussetzung dafür sind proxy_http_version 1.1 und ein geleerter Connection-Header, da HTTP/1.0-Verbindungen keine Wiederverwendung unterstützen und der Connection-Header sonst hop-by-hop-Semantik durchreicht, die die Wiederverwendung verhindert. Für Setups mit Load Balancern vor mehreren Backend-Instanzen gilt dasselbe Prinzip: Ein Pool persistenter Verbindungen pro Backend-Ziel ist strikt günstiger als ein Verbindungsaufbau pro weitergeleitetem Request.


# nginx.conf - upstream keepalive pool for a reverse-proxied backend service (e.g. search API)
upstream opensearch_backend {
    server 10.0.1.10:9200;
    server 10.0.1.11:9200;
    keepalive 32;              # persistent connection pool, reused across client requests
    keepalive_timeout 60s;
    keepalive_requests 500;
}

server {
    location /internal/search/ {
        proxy_pass http://opensearch_backend;
        proxy_http_version 1.1;          # keepalive requires HTTP/1.1
        proxy_set_header Connection "";  # clear hop-by-hop Connection header
    }
}

5. Connection Pooling fuer Backend-Services: Payment, Suche, Redis, MySQL

Ein typischer Magento-Request auf PHP-FPM verursacht selbst mehrere ausgehende Verbindungen: einen Aufruf an ein Payment-Gateway für Zahlungsstatus, eine Suchanfrage an OpenSearch oder Elasticsearch für die Kategorieseite, mehrere Redis-Zugriffe für Cache und Session, und mindestens eine MySQL-Verbindung für Produktdaten. Jede dieser Verbindungen, die naiv pro Request neu aufgebaut wird, zahlt den vollen TCP- und gegebenenfalls TLS-Handshake-Preis aus Abschnitt eins, multipliziert mit der Anzahl gleichzeitiger Requests auf dem Server.

Weil PHP-FPM Requests in isolierten, kurzlebigen Prozessen abarbeitet, ist echtes Connection Pooling im Sinne eines gemeinsam genutzten Verbindungs-Pools über mehrere Worker hinweg nicht ohne Weiteres möglich, wie es etwa Node.js oder ein Java-Application-Server bieten würde. Was funktioniert, sind persistente Verbindungen innerhalb eines einzelnen FPM-Worker-Prozesses, die über das Ende eines Requests hinaus bestehen bleiben und beim nächsten Request, der von demselben Worker bearbeitet wird, wiederverwendet werden. Für die Überwachung, ob diese Wiederverwendung tatsächlich greift, zeigt ss -tnp | grep :6379 die Anzahl aktiver Verbindungen zu Redis, während redis-cli INFO stats mit dem Feld total_connections_received misst, wie oft überhaupt neu verbunden wurde.

6. Naiver PHP-Code: warum ein neuer cURL/Guzzle-Client pro Request Overhead verursacht

Ein häufiges Muster in gewachsenem PHP-Code: Eine neue Guzzle- oder cURL-Instanz wird direkt in der Methode erzeugt, die den externen Aufruf macht, statt sie einmal zu instanziieren und wiederzuverwenden. new Client() innerhalb einer Payment- oder Such-Integration, die mehrfach pro Request aufgerufen wird, erzeugt bei jedem Aufruf ein frisches Set an curl-Handles ohne Wissen über bereits offene Verbindungen zum selben Host. Das Ergebnis: Selbst innerhalb eines einzigen PHP-Requests mit drei sequentiellen Aufrufen an dieselbe API werden drei vollständige TCP- und TLS-Handshakes bezahlt, obwohl einer gereicht hätte.

Die Lösung ist banal, wird in der Praxis aber oft übersehen: Der HTTP-Client wird einmal instanziiert, etwa als Singleton über Dependency Injection, und für alle Aufrufe innerhalb desselben Requests wiederverwendet. Guzzles Standard-Handler mit CurlMultiHandler hält die zugrundeliegenden curl-Verbindungen zwischen Aufrufen offen, solange dieselbe Client-Instanz verwendet wird. Wichtig zu verstehen: Diese Wiederverwendung wirkt nur innerhalb der Lebensdauer eines einzelnen PHP-Prozesses, also eines einzelnen Requests bei klassischem PHP-FPM. Für Wiederverwendung über Requests hinweg braucht es zusätzlich persistente Verbindungen oder einen Worker-basierten Runtime wie Swoole oder RoadRunner, der PHP-Prozesse langlebig hält.


<?php
declare(strict_types=1);

namespace Mironsoft\Payment\Client;

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlMultiHandler;

/**
 * Wrong: a fresh Guzzle client (and fresh curl handles) is created on every call,
 * paying a full TCP + TLS handshake for each request even to the same host.
 */
final class NaivePaymentClient
{
    public function charge(array $payload): array
    {
        $client = new Client(); // new connection pool created and discarded each call
        $response = $client->post('https://api.payment-gateway.example/v1/charge', [
            'json' => $payload,
        ]);
        return json_decode((string) $response->getBody(), true);
    }
}

/**
 * Right: one shared Guzzle client instance (injected via DI) reuses its
 * underlying curl handles across all calls within the same request lifecycle.
 */
final class PooledPaymentClient
{
    private Client $client;

    public function __construct()
    {
        $this->client = new Client([
            'base_uri' => 'https://api.payment-gateway.example/',
            'timeout' => 5.0,
            // CurlMultiHandler keeps curl handles alive between requests
            'handler' => HandlerStack::create(new CurlMultiHandler()),
        ]);
    }

    public function charge(array $payload): array
    {
        $response = $this->client->post('v1/charge', ['json' => $payload]);
        return json_decode((string) $response->getBody(), true);
    }
}

7. Persistente Verbindungen richtig nutzen: Redis, PDO und die Tradeoffs

Für Redis unterstützt die phpredis-Extension mit pconnect() statt connect() persistente Verbindungen, die über das Ende eines Requests hinaus im PHP-FPM-Worker-Prozess bestehen bleiben und beim nächsten Request, den derselbe Worker bearbeitet, direkt wiederverwendet werden, ohne neuen TCP-Handshake oder erneute AUTH-Authentifizierung. Magentos eigene Redis-Cache-Konfiguration in env.php unterstützt das nativ über die Option 'persistent' => '_magentoPersistentRedis' im Cache-Backend-Block, wodurch der Cache-Adapter automatisch persistente Verbindungen mit einem eindeutigen Identifier nutzt.

Der Tradeoff: Bei N parallelen FPM-Workern entstehen bis zu N dauerhaft offene Verbindungen zum Redis-Server, selbst wenn gerade kein Request aktiv ist. Redis' maxclients-Limit muss entsprechend groß dimensioniert sein, sonst schlagen neue Verbindungsversuche mit "ERR max number of clients reached" fehl, sobald die Worker-Anzahl skaliert. Für MySQL gilt mit PDO::ATTR_PERSISTENT ein ähnliches Prinzip und ein ähnliches Risiko: Persistente PDO-Verbindungen sparen Handshake-Zeit, können aber Transaktionszustand, gesperrte Tabellen oder Session-Variablen aus einem vorherigen Request in den nächsten durchreichen, wenn nicht explizit zurückgesetzt wird. In der Praxis empfiehlt sich PDO::ATTR_PERSISTENT nur mit sorgfältigem Monitoring von max_connections, oder alternativ ein externer Connection-Pooler wie ProxySQL vor der Datenbank.


<?php
declare(strict_types=1);

// Redis: persistent connection survives across requests handled by the same
// PHP-FPM worker process, avoiding a new TCP handshake + AUTH on every request.
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379, 2.5, 'fpm-worker-pool-1');
$redis->auth('secret-password');
$redis->select(1);

// PDO: ATTR_PERSISTENT reuses the MySQL connection across requests too,
// but beware of leaked transaction state and session variables between requests.
$pdo = new PDO(
    'mysql:host=127.0.0.1;dbname=magento;charset=utf8mb4',
    'magento_user',
    'secret-password',
    [
        PDO::ATTR_PERSISTENT => true,
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]
);

// Always reset session-scoped state explicitly when reusing persistent connections
$pdo->exec('SET SESSION sql_mode = "STRICT_TRANS_TABLES"');

8. Domain-Sharding: ein Anti-Pattern aus der HTTP/1.1-Ära

Browser haben unter HTTP/1.1 historisch die Anzahl gleichzeitiger Verbindungen pro Hostname begrenzt, üblicherweise auf sechs. Für Seiten mit vielen statischen Ressourcen, wie Produktbildern, CSS- und JavaScript-Dateien, wurde diese Grenze schnell zum Flaschenhals: Selbst wenn die Bandbreite ausgereicht hätte, konnten nicht mehr als sechs Dateien gleichzeitig von derselben Domain geladen werden. Als Umgehung etablierte sich Domain-Sharding: Statische Assets wurden künstlich auf mehrere Subdomains wie static1.example.com, static2.example.com und img.example.com verteilt, sodass der Browser für jede Subdomain wieder sechs eigene Verbindungen aufbauen durfte, in Summe also deutlich mehr Parallelität.

Der Preis dafür wurde in der HTTP/1.1-Ära gerne verschwiegen: Jede zusätzliche Subdomain erfordert einen eigenen DNS-Lookup, einen eigenen TCP-Handshake und bei HTTPS einen eigenen TLS-Handshake, alle mit dem vollen Overhead aus Abschnitt eins. Vier Shards bedeuten vier separate Verbindungsaufbauten, die sich nicht gegenseitig wiederverwenden lassen, selbst wenn sie letztlich vom selben physischen Server oder CDN-Edge bedient werden. Auf einer Verbindung mit hoher Latenz kann dieser zusätzliche Aufbau-Overhead die durch Parallelität gewonnene Zeit teilweise oder vollständig wieder auffressen.

9. HTTP/2/HTTP/3-Multiplexing und was das fuer Magento/Hyva bedeutet

HTTP/2 hat den ursprünglichen Grund für Domain-Sharding beseitigt: Über eine einzige TCP-Verbindung können beliebig viele Streams parallel und ohne gegenseitige Blockierung übertragen werden, sogenanntes Multiplexing. Der Browser braucht also keine sechs Verbindungen pro Host mehr, um Parallelität zu erreichen, er kann Dutzende Ressourcen gleichzeitig über eine einzige, bereits aufgebaute Verbindung anfordern. HTTP/3 auf Basis von QUIC über UDP geht noch weiter und beseitigt auch Head-of-Line-Blocking auf Transportebene, das bei HTTP/2 durch TCP-Paketverlust weiterhin auftreten kann. Domain-Sharding ist unter beiden Protokollen nicht nur überflüssig, sondern aktiv kontraproduktiv: Es erzwingt zusätzliche Handshakes, verhindert Verbindungswiederverwendung und zerschneidet den Multiplexing-Vorteil in genau so viele Stücke, wie es Shards gibt.

Für einen Magento- und Hyva-Stack bedeutet das konkret: Statische Assets, CDN und die Hauptdomain sollten wo möglich auf einen einzigen, HTTP/2- oder HTTP/3-fähigen Origin konsolidiert werden, statt historisch gewachsene static1/static2-Subdomains weiterzuführen. Auf Nginx-Ebene lässt sich das mit listen 443 ssl http2; aktivieren, beim CDN-Anbieter ist HTTP/2 heute in der Regel Standard und muss nur nicht durch veraltete Legacy-Konfiguration blockiert werden. Für Backend-Service-Clients gilt dieselbe Konsolidierungslogik: ein wiederverwendeter HTTP-Client statt vieler einzelner Instanzen, persistente Verbindungen zu Redis und MySQL statt Neuverbindung pro Request, und ein Upstream-Keep-Alive-Pool zwischen Nginx und internen Diensten. Das folgende di.xml-Beispiel zeigt, wie ein wiederverwendbarer HTTP-Client sauber über Dependency Injection bereitgestellt wird, statt ihn in jeder Klasse neu zu instanziieren.


<?xml version="1.0"?>
<!-- di.xml: register a shared, connection-pooled HTTP client for outbound API calls -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <virtualType name="Mironsoft\Payment\Client\PooledGuzzleClient" type="GuzzleHttp\Client">
        <arguments>
            <argument name="config" xsi:type="array">
                <item name="base_uri" xsi:type="string">https://api.payment-gateway.example/</item>
                <item name="timeout" xsi:type="number">5</item>
                <!-- shared handler keeps curl connections alive across calls -->
                <item name="handler" xsi:type="object">Mironsoft\Payment\Client\PooledHandlerStack</item>
            </argument>
        </arguments>
    </virtualType>

    <type name="Mironsoft\Payment\Gateway\ChargeService">
        <arguments>
            <argument name="httpClient" xsi:type="object">Mironsoft\Payment\Client\PooledGuzzleClient</argument>
        </arguments>
    </type>
</config>

Jede der vorgestellten Techniken adressiert denselben Grundmechanismus aus einer anderen Perspektive: einen bereits bezahlten Verbindungsaufbau möglichst oft wiederzuverwenden, statt ihn erneut zu bezahlen. Die folgende Übersicht fasst die Kostenunterschiede zusammen.

Szenario Ohne Pooling/Keep-Alive Mit Pooling/Keep-Alive Empfohlene Einstellung
Neue HTTPS-Verbindung (TLS 1.2) 2-3 RTT vor erstem Byte 0 RTT bei Wiederverwendung TLS 1.3 aktivieren, Session-Resumption
Sequentielle Requests an denselben Host Handshake bei jedem Request Eine Verbindung für alle Requests keepalive_requests >= 1000
PHP-FPM zu Redis Neuer Connect + Auth pro Request Persistente Verbindung pro Worker pconnect() mit maxclients-Tuning
Nginx zu Backend-Upstream Neue Verbindung pro Client-Request Wiederverwendeter Upstream-Pool keepalive 32; im upstream-Block
Statische Assets über mehrere Subdomains DNS + Handshake pro Shard Ein gebündelter, multiplexed Origin Domain-Sharding entfernen, HTTP/2 nutzen

Mironsoft

Netzwerk-Performance, Connection Pooling und Backend-Tuning für Magento-Shops

Verbindungs-Overhead in eurem Stack eliminieren?

Wir analysieren Handshake-Overhead, Keep-Alive-Konfiguration und Connection Pooling in eurem Magento- und Hyva-Stack, von Nginx über PHP-FPM bis zu Redis, MySQL und externen APIs.

Netzwerk-Audit

Analyse von TCP/TLS-Handshakes, Keep-Alive und Multiplexing im Traffic

Backend-Tuning

Connection Pools für Redis, MySQL, OpenSearch und Payment-APIs konfigurieren

Monitoring-Setup

Connection-Reuse-Metriken und Alerts für Regressionen einrichten

10. Zusammenfassung

Connection Pooling und Keep-Alive lösen dasselbe Grundproblem aus unterschiedlichen Blickwinkeln: Ein bereits bezahlter Verbindungsaufbau, TCP-Handshake plus gegebenenfalls TLS-Handshake, ist zu teuer, um ihn bei jedem einzelnen Request neu zu bezahlen. HTTP Keep-Alive hält client-seitige Verbindungen über mehrere Requests offen, keepalive_timeout und keepalive_requests steuern dabei die Balance zwischen Wiederverwendung und Ressourcenverbrauch. Upstream-Keep-Alive-Pools übertragen dasselbe Prinzip auf die Verbindung zwischen Reverse Proxy und Backend-Diensten.

Auf PHP-Ebene verursachen frisch instanziierte Guzzle- oder cURL-Clients unnötigen Handshake-Overhead, während persistente Verbindungen zu Redis und MySQL diesen Aufwand innerhalb eines FPM-Worker-Prozesses vermeiden, allerdings mit klaren Tradeoffs bei Connection-Limits und Session-Zustand. Domain-Sharding, einst eine sinnvolle Umgehung der HTTP/1.1-Verbindungsgrenze, ist mit HTTP/2- und HTTP/3-Multiplexing zu einem reinen Anti-Pattern geworden, das zusätzliche Handshakes erzwingt, statt sie zu vermeiden. Ein konsolidierter, gut konfigurierter Stack aus wenigen, wiederverwendeten Verbindungen schlägt die alte Strategie vieler paralleler, kurzlebiger Verbindungen in jeder gemessenen Metrik.

Connection Pooling und Keep-Alive - Das Wichtigste auf einen Blick

TCP/TLS-Handshake

Jede neue Verbindung kostet 1-3 RTT vor dem ersten Byte. TLS 1.3 reduziert das auf 1 RTT, Session-Resumption auf 0 RTT.

HTTP Keep-Alive

keepalive_timeout und keepalive_requests im Webserver, keepalive im upstream-Block für Backend-Pools.

Backend-Connection-Pooling

Persistente Verbindungen zu Redis, MySQL und Suche vermeiden wiederholten Handshake-Overhead pro PHP-FPM-Request.

Kein Domain-Sharding mehr

HTTP/2 und HTTP/3-Multiplexing machen die alte 6-Connections-pro-Host-Umgehung überflüssig und kontraproduktiv.

11. FAQ: Connection Pooling und Keep-Alive

1Warum kostet eine neue HTTPS-Verbindung mehrere Umlaufzeiten?
Vor dem ersten Anwendungs-Byte müssen TCP-Handshake (SYN, SYN-ACK, ACK) und TLS-Handshake mit Zertifikatsaustausch und Schlüsselverhandlung abgeschlossen sein. Je nach TLS-Version sind das ein bis drei zusätzliche Umlaufzeiten.
2Was ist der Unterschied zwischen TLS 1.2 und TLS 1.3?
TLS 1.2 braucht standardmäßig zwei Umlaufzeiten, TLS 1.3 nur eine, weil Schlüsselmaterial bereits im ersten Client Hello mitgeschickt wird. Mit Session-Resumption ist bei TLS 1.3 sogar 0-RTT möglich.
3Was macht HTTP Keep-Alive konkret?
Hält eine aufgebaute TCP/TLS-Verbindung offen und erlaubt mehrere sequentielle Requests über dieselbe Verbindung, statt für jeden Request neu zu verhandeln. Seit HTTP/1.1 Standardmodus.
4Wofür sind keepalive_timeout und keepalive_requests?
keepalive_timeout steuert, wie lange eine inaktive Verbindung offen bleibt. keepalive_requests begrenzt Requests pro Verbindung, bevor sie neu aufgebaut wird, um Worker-Ressourcen fair zu verteilen.
5Was ist ein Upstream-Keep-Alive-Pool?
Ein Pool bereits offener, persistenter Verbindungen zwischen Reverse Proxy und Backend-Dienst. keepalive im upstream-Block plus proxy_http_version 1.1 macht das möglich.
6Warum verursacht ein neuer Guzzle-Client pro Aufruf Overhead?
Jede neue Instanz erzeugt neue curl-Handles ohne Wissen über bereits offene Verbindungen, wodurch pro Aufruf erneut ein voller Handshake nötig ist. Ein wiederverwendeter Client vermeidet das.
7Was sind die Tradeoffs von persistenten Redis-Verbindungen?
pconnect() spart Handshake-Overhead über mehrere Requests hinweg, kann aber bei vielen Workern die maximale Client-Anzahl auf dem Redis-Server überschreiten. maxclients muss entsprechend dimensioniert sein.
8Was ist Domain-Sharding und warum wurde es früher genutzt?
Verteilung statischer Assets auf mehrere Subdomains, um die HTTP/1.1-Grenze von rund sechs Verbindungen pro Host zu umgehen und mehr parallele Downloads zu ermöglichen.
9Warum ist Domain-Sharding mit HTTP/2 kontraproduktiv?
HTTP/2 und HTTP/3 multiplexen beliebig viele Requests über eine Verbindung. Sharding erzwingt stattdessen zusätzliche DNS-Lookups und Handshakes und verhindert Verbindungswiederverwendung.
10Wie überwacht man Connection-Reuse in Produktion?
ss oder netstat zeigen aktive Verbindungen zu Redis/MySQL. redis-cli INFO stats liefert total_connections_received gegen connected_clients als direktes Reuse-Signal.