Lassen Sie Lack alte Daten aus dem Cache senden, während er neue abruft?

8

Ich speichere dynamisch generierte Seiten (PHP-FPM, NGINX) zwischen und habe Lack vor mir, das funktioniert sehr gut.

Sobald das Cache-Timeout erreicht ist, sehe ich Folgendes:

  • Seite für neue Kundenanfragen
  • Lack erkennt das Cache-Timeout
  • Kunde wartet
  • Lack holt neue Seite aus dem Backend
  • Lack liefert neue Seite an den Client (und hat auch Seite für die nächste Anfrage zwischengespeichert, die es sofort erhält)

Was ich tun möchte ist:

  • Client-Anforderungsseite
  • Lack erkennt das Timeout
  • Lack liefert alte Seite an den Kunden
  • Lack holt neue Seite aus dem Backend und legt sie im Cache ab

In meinem Fall ist es keine Site, auf der veraltete Informationen ein so großes Problem darstellen, insbesondere nicht, wenn es sich um ein Cache-Timeout von wenigen Minuten handelt.

Ich möchte jedoch nicht, dass der bestrafte Benutzer in der Schlange steht und etwas Sofortiges liefert. Ist das irgendwie möglich?

Zur Veranschaulichung hier eine Beispielausgabe der 5-minütigen Belagerung meines Servers, die für den Cache für eine Minute konfiguriert wurde:

HTTP/1.1,200,  1.97,  12710,/,1,2013-06-24 00:21:06
...
HTTP/1.1,200,  1.88,  12710,/,1,2013-06-24 00:21:20
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:22:08
...
HTTP/1.1,200,  1.89,  12710,/,1,2013-06-24 00:22:22
...
HTTP/1.1,200,  1.94,  12710,/,1,2013-06-24 00:23:10
...
HTTP/1.1,200,  1.91,  12709,/,1,2013-06-24 00:23:23
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:24:12
...

Ich habe die Hunderte von Anfragen, die eingegangen sind, ausgelassen 0.02. Aber es macht mir immer noch Sorgen, dass es Benutzer geben wird, die fast 2 Sekunden auf ihren rohen HTML-Code warten müssen.

Können wir es hier nicht besser machen?

(Ich bin beim Cache auf Varnish Send gestoßen . Es klang ähnlich, aber nicht genau das, was ich versuche.)

Lösung

Die Antwort von Shane Madden enthielt die Lösung, aber ich habe sie nicht sofort erkannt. Es gab ein weiteres Detail, das ich nicht in meine Frage aufgenommen habe, weil ich dachte, es sei nicht relevant, aber tatsächlich ist es das.

Die CMS-Lösung, die ich derzeit verwende, verfügt über einen Lackdatenbank-Listener und kann somit Lack benachrichtigen, um Seiten zu sperren, deren Inhalt sich geändert hat. Es wurde eine PURGEAnfrage mit einem regulären Ausdruck gesendet, um bestimmte Seiten zu sperren.

Zusammenfassend gibt es zwei Fälle, in denen ich unglückliche Benutzer hatte:

  1. Die normale Lack-TTL einer Seite läuft ab
  2. Backend-Benutzer ändern den Inhalt. Dadurch wird eine Bereinigungsanforderung an den Lack gesendet

In beiden Fällen habe ich "unglückliche" Benutzer. Im zweiten Fall wird dies durch die Tatsache gemildert, dass Backend-Benutzer die Seite normalerweise überprüfen, nachdem sie geändert wurde. aber nicht unbedingt.

Trotzdem habe ich für den zweiten Fall eine Lösung erstellt (ja, mir ist klar, dass diese Frage mit der Suche nach einer Antwort für den ersten Fall begann ... schlecht formulierte Frage meinerseits):

Anstatt eine Bereinigungsanforderung zu senden, habe ich den Shanes-Vorschlag verwendet und die VCL so angepasst, dass mein Lackdatenbank-Listener eine spezielle Anforderung zum Abrufen einer Seite mit der hash_always_missEinstellung auf senden kann true.

Mit der aktuellen Architektur habe ich nicht wirklich den Luxus, eine echte asynchrone Anfrage zu stellen, aber mit Hilfe von Wie mache ich eine asynchrone GET-Anfrage in PHP? Ich konnte eine GET-Anforderung zum Lackieren erstellen, die nicht auf das Laden der Seite wartet, aber gut genug ist, um den Lack auszulösen, um die Seite aus dem Backend abzurufen und zwischenzuspeichern.

Der Nettoeffekt war, dass der Datenbank-Listener die Anfrage an Lack gesendet hat und während ich die bestimmte Seite abgefragt habe, meine Anfragen nie "unglücklich" gemacht wurden, aber sobald Lack die Seite vollständig aus dem Backend abgerufen hat (dies kann zwischen 300 ms und 2 s liegen) war plötzlich da.

Ich muss noch eine Lösung finden, um die gleichen Probleme zu vermeiden, wenn die normale TTL ausgeht, aber ich denke, die Lösung ist auch genau so, wie Shane es vorschlägt: Wenn ich wget verwende, um das auszulösen hash_always_miss, muss ich nur klug genug sein, um eine Liste zu erhalten von Seiten muss ich aktualisieren.

Kennzeichen
quelle

Antworten:

3

Die Lösung, mit der ich dieses Problem gelöst habe, besteht darin, sicherzustellen, dass die TTL auf einer Seite vor dem Aktualisieren nie abläuft. Dadurch wird ein HTTP-Client, der auf einem meiner Systeme ausgeführt wird, gezwungen, die langsame Last anstelle eines unglücklichen Clients zu erhalten Anfrage.

In meinem Fall handelt es sich dabei um wgeteinen Cron, der einen speziellen Header sendet, um die Anforderungen zu markieren, und die darauf req.hash_always_missbasierende Einstellung , wodurch eine neue Kopie des Inhalts gezwungen wird, in den Cache abgerufen zu werden.

acl purge {
    "localhost";
}

sub vcl_recv {
    /* other config here */
    if (req.http.X-Varnish-Nuke == "1" && client.ip ~ purge) {
        set req.hash_always_miss = true;
    }
    /* ... */
}

Für Ihren Inhalt bedeutet dies möglicherweise, dass Sie die Lack-TTL auf etwa 5 Minuten einstellen, aber ein Cron-Wget so konfiguriert ist, dass jede Minute eine Cache-Aktualisierungsanforderung gestellt wird.

Shane Madden
quelle
Ich glaube ich verstehe was du meinst. Das würde gut für eine einzelne Seite funktionieren, aber die anderen Tausenden? Cron / Wget kann auf dieser Skala nicht verwendet werden.
Markieren Sie den
Eigentlich müssen Sie mindestens angeben, welche Seiten im Cache aktuell bleiben sollen. Angesichts dieser Liste gibt es keinen Grund, warum wget in einem Cron-Skript Ihnen nicht helfen kann.
Falcon Momot
Ich denke, das ist die Herausforderung bei diesem "externen Lackansatz". Ich muss mich letztendlich hinsetzen und eine Entscheidung treffen und dies für jede Seite umsetzen. Es gibt keine Möglichkeit, dem Lack zu sagen: "ttl ist ausgegangen? Neue Version holen, aber bis dahin die alte bedienen" ...? Hmm.
Markieren Sie den
4

@BEARBEITEN:

Nur eine kurze Beschreibung, um Ihnen mitzuteilen, dass diese Funktion anscheinend gerade erst in der neuesten Version im Hauptzweig implementiert wurde. Möglicherweise unterstützt Ihre Version noch kein echtes Stale-While-Revalidate / das von mir veröffentlichte Beispiel würde dienen 9999/10000 Anfragen mit einem armen Mistkerl, der noch warten muss, bis die Anfrage im Backend abgeschlossen ist (immer noch besser als nichts;) ...


Nun, ich bin nicht 100% sicher, warum die vorherigen Kommentare sagen, dass es nicht funktioniert, aber laut: https://www.varnish-software.com/static/book/Saving_a_request.html

  • req.grace - Definiert, wie lange ein Objekt für Varnish überfällig sein kann, um es weiterhin für den Grace-Modus zu berücksichtigen.
  • beresp.grace - Definiert, wie lange nach der beresp.ttl-Zeit ein Objekt aufbewahrt wird.
  • req.grace - wird in vcl_recv häufig basierend auf dem Status des Backends geändert.

Ich verwende derzeit eine Konfiguration wie im Handbuch beschrieben und sie funktioniert einwandfrei ... Hier ist ein Ausschnitt meiner vcl-Datei ...

sub vcl_recv {
    # Cache rules above here...
    if (req.backend.healthy) {
        set req.grace = 30d;
    } else {
        set req.grace = 300d;
    }
}

sub vcl_fetch {
    # Fetch rules above here ...

    # If backend returns 500 error then boost the cache grace period...
    if (beresp.status == 500) {
        set beresp.grace = 10h;
        return (restart);
    }

    # How long carnish should keep the objects in cache..
    set beresp.grace = 1h;

    # Actual TTL of cache - If an object is older than this an update will be triggered to the backend server :)
    set beresp.ttl = 1m;
}

Beachten Sie, dass Sie die Backend-Prüfung einrichten müssen, wenn Sie eine längere Nachfrist für die Backend-Antwort (für 500 Fehler wie in meiner Konfiguration) bereitstellen möchten ... Hier ist eine Kopie meiner Backend-Prüfung.

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = { 
        .url = "/nginx-status";
        .timeout = 500 ms; 
        .interval = 3s; 
        .window = 10;
        .threshold = 4;
    }
}
David
quelle
ist das noch gültig für vcl 4.0?
Gadelkareem
0

In Lack 3 wird dies über den "Grace-Modus" erreicht. Gemäß dem Handbuch [1] müssen Sie die folgende Logik hinzufügen:

sub vcl_fetch {
  set beresp.grace = 30m;
} // Makes objects to be cached/stored 30 min beyond its max-age/ttl

sub vcl_recv {
  set req.grace = 60s;
} // Allows varnish to serve objects which expired within last minute.

[1] https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode

NITEMAN
quelle
Dies wird das Ziel von OP nicht erreichen. Ein Client wird immer noch verzögert darauf warten, dass der Inhalt aus dem Backend abgerufen wird, obwohl anderen Clients, die dahinter stehen, das veraltete Gnadenergebnis zugestellt wird. "Sobald die TTL abläuft, sollte der erste Client, der nach dem Inhalt fragt, 15 Sekunden lang hängen bleiben, während der zweite Client die Kopie erhalten sollte." - Lack-software.com/static/book/Saving_a_request.html
Pax
Entschuldigung, ich habe nicht bemerkt, wie es mit der ersten abgelaufenen Anfrage tatsächlich funktioniert.
NITEMAN
Vielleicht kann das gewünschte Ziel mit etwas ttl Cheat & Inline C erreicht werden, um eine "Hintergrundanfrage"
auszulösen