Varnish-Cache – einer für alle

Kunde möchte eine voraussichtlich stark frequentierte Seite mit Typo3 bei sich hosten. 
Erwartet werden 5000 Benutzer gleichzeitig – die geschätzt alle 10 Sekunden klicken. Ergibt 500 Requests/s. Wir sollen die Architektur vorschlagen.

Mein Metier. Nach einer zweiwöchigen Evaluation kristallisierte sich Nginx mit FastCGI als Webserver heraus, weil der FastCGI Anfragen cachen kann und somit auf eine tolle Geschwindigkeit kommt.

Das Problem ist nicht der Cache an sich. Das Problem ist, wie Nginx damit umgeht, wenn der Cache verfällt.

Der Cache hat ein Ablaufdatum, eine Time-To-Live, TTL. Läuft diese ab, muss das HTML vom Backend neu geholt werden. Und wenn der Cache bei 500 gleichzeitigen Anfragen pro Sekunde abläuft, muss das HTML 500 mal gleichzeitig vom Backend geholt werden. Schönä!

2842-face down.png

Ich habe verschiedene Reverse-Proxies ausprobiert. Unter anderem den Varnish-Cache. Und der hat eine Besonderheit, die ich sonst bei keinem Cache finde:

When several clients are requesting the same page Varnish will send one request to the backend and place the others on hold while fetching one copy from the back end. (Source)

2844-face up-thumb-500x264-2843.png

Ich beglücke den Webserver mit 5000 Benutzern. Gleichzeitig.

5000 Benutzer prasseln auf den Server. Nach einer Minute wird _eine_ Anfrage an den Webserver geschickt – die 5000 Benutzer werden weiterhin mit dem „alten“ Inhalt bedient.
Die Antwort kommt, der Cache wird erneuert und von jetzt an wird der neue Inhalt an die 5000 Benutzer ausgeliefert.

So sieht die Aktivität auf dem Server aus:

2845-varnish_protects.png

Genau ein httpd ist am schwitzen, und nicht fünfzig. Und der varnishd tobt sich aus…

So sieht meine Netzwerkauslastung aus:

2846-bw.png

I can haz more bandwidth?

Zwei Proxysniffer im Verbund auf Vollgas. Schampar schade dass unser Netzwerk nicht mehr als 800MBit hergeben will:

2848-AnalyseLoadtestResultTotalNetworkImageWeblet-thumb-500x255-2847.gif

Ça fait rock.

Und der Varnish lässt sich unendlich flexibel konfigurieren. Ich kann z.B. anhand der URL Anfragen auf unterschiedliche Backends leiten, mehrere Backends mit einer gewichteten Lastverteilung aufstellen und und und…

Ein Beispiel.

backend crappy_j2ee_enterprise_grüselapp {
    .host = "server.example.com";
    .first_byte_timeout = 5s;
    .max_connections = 10;
    .probe = {
        .url = "/check.jsp";
        .interval = 5s;
        .timeout = 1s;
        .window = 5;
        .threshold = 3;
    }
}

Das Backend wird alle 5 Sekunden vom Varnish angefragt (interval). Von 5 Anfragen (window) müssen 3 korrekt sein (threshold) und unter 1s antworten (timeout).

Ist dies nicht der Fall, bekommt das Backend den Status „unhealthy“ verpasst. Jetzt liegt es an der Konfiguration, was jetzt passiert.

if (! req.backend.healthy) {
    set req.grace = 30d;
} else {
    set req.grace = 1m; 
}

Ist das Backend nicht verfügbar, wird es für 30 Tage nicht mehr angefragt bei einem User-Request, auch wenn die TTL für die Seite abgelaufen ist. Der Health-Check läuft normal weiter.

#renew content every minute
set beresp.ttl = 1m;

#save content for 30 days - if backend fails
set beresp.grace = 30d;

Nach einer Minute wird der Webserver nach neuen Inhalten abgefragt. Die Antwort wird 30 Tage lang gespeichert – sollte der Webserver abliegen, kann die Website noch 30 Tage lang von dieser Notration überleben.

Und das ist toll – ich kann meine Apache’s stoppen. Ein paar Benutzer haben Pech – in den maximal 5 Sekunden, bis Varnish das merkt und das Backend als ungesund deklariert. Aber dann läuft alles wie gewohnt weiter. Sobald die Webserver wieder da sind, werden die Cache-Inhalte erneuert.

Voraussetzung für den ganzen Gspass ist natürlich eine statische Seite ohne personalisierte Inhalte.

Man könnte es noch auf die Spitze treiben mit dem Caching und für verschiedene Inhalte eigene TTLs definieren:

2849-esi.png

Did you know? Wikileaks setzt auch auf Varnish:

2850-varnishleaks.png

Ich weiss nicht wie viel Traffic Varnish wirklich verarbeiten kann. Aber ganz offensichtlich mehr als 10 Gigabit:

2852-Bildschirmfoto 2010-12-17 um 10.58.07-thumb-500x185-2851.png

http://twitter.com/wikileaks/status/9609091915718656

http://www.varnish-cache.org/

5 Gedanken zu “Varnish-Cache – einer für alle

  1. Danke Claude. Sehr cool (und nützlich). Ich hasste es immer mich mit der höllisch komplizierten Konfiguration von Squid rumzuärgern. Weisst Du bei Varnish grad, ob ich auch SSL termineren kann?

    So als Lab-Projekt nicht schlecht: Ein paar (viele) Varnish-Instanzen auf Amazon AWS und die dort mit dem „Elastic Load Balancing“ verteilen… und die holen sie sich den Content (lazy) ab der Quelle. Und dann einen Lasttest der das Ding runter holt.

    PS: Wir hätten sonst auch Proxy Sniffer Instanzen auf AWS wenn Du ein bisschen mehr Saft brauchst ;-)

  2. Nope, SSL kann man damit nicht terminieren: http://www.mail-archive.com/varnish-misc@projects.linpro.no/msg00878.html

    Läck Tschonny. Da macht jemand ein Varnish-Setup, welches 275’000 Requests/Sekunde beantworten kann: http://kristianlyng.wordpress.com/2010/10/23/275k-req/

    …und ich dachte, 10’000 Anfragen pro Sekunde seien viel…

    Interessant auch: „The most obvious bottleneck during a test like this is bandwidth.“

    Dieses geniale Stück Software nutzt und reizt die Hardware wirklich bis zum letzten Bit aus…

  3. Erstaunlich, dass die 5000 User von einer VMware-Instanz mit 1 Proz. und 1GB Memory beantwortet werden können.
    Klasse Lösung mit dem Varnish-Cache. Gratulation Claude!

  4. Super Artikel, Danke Claude.

    Da ich mittlerweile selbst viel mit TYPOI3 arbeite ist es wirklich spannend zu sehen was man da raus holen kann. Allerdings muss ich mich da noch mal mehr einlesen in die Thematik. Super ist die Ergänzung in deinem Blog.

    Mehr solche Artikel!

    Grüße aus Hamburg
    Felix

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>