Response Cache

Cache Controller

Der Response Cache ist ein serverseitiger Cache und ermöglicht ähnlich schnelle Request-Zeiten wie statischer Server-Content.

  1. Response Cache Control
  2. Response Cache Store
  3. Charakteristiken

Response Cache Control

Du kannst n2n über ControllerAdapter::assignResponseCacheControl() anweisen, den generierten Response für den aktuellen Pfad im Response Cache zwischenzuspeichern. Beim nächsten Aufruf des selben Pfads (mit selbem Subsystem und gleicher HTTP-Methode) wird n2n diesen gespeicherten Response direkt aus dem Cache lesen und ausgeben, ohne irgendwelche Controller, Models, Entities usw. zu initialisieren. Dies verkürzt die Wartezeiten für den Benutzer erheblich.

class ExampleController extends ControllerAdapter {    
    public function doFoo() {
        $this->assignResponseCacheControl(new \DateInterval('PT30M'));
        
        $this->forward('view\bar.html');
    }
}

assignResponseCacheControl() erwartet als ersten Parameter das Intervall, in dem der zwischengespeicherte Response wieder erneuert werden muss. Im oben stehenden Beispiel wird der Response für http://.../foo 30 Minuten zwischengespeichert. Die URL-Query wird dabei ignoriert. Der gleiche zwischengespeicherte Response wird also auch beim Aufruf von http://.../foo?param=bar ausgegeben.

Du kannst den Cache manuell löschen, indem du das Verzeichnis var/tmp/n2n/responsecache leerst. Wie du das innerhalb der Applikation tun kannst, zeigt der Abschnitt Response Cache Store.

Möchtest du, dass auch die URL-Query berücksichtigt wird, kannst du als zweiten Parameter true übergeben. Berücksichtigt werden alle Query-Werte, die der aktuellen Controller-Methode als Parameter übergeben werden.

    public function doFoo(ParamQuery $param = null) {
        $this->assignResponseCacheControl(new \DateInterval('PT1M'), true);
        
        $this->forward('view\bar.html');
    }

In diesem Beispiel wird der Response für http://.../foo und http://.../foo?param=bar  nun individuell zwischengespeichert. Die aufgerufene URL muss nun aber exakt mit der URL übereinstimmen, für die der Response zwischengespeichert wurde, damit n2n den zwischengespeicherten Response ausgibt. Rufst du doFoo() zum Beispiel über http://.../foo?param=bar&param2=baz auf, wird zwar mit jedem Aufruf eine neue Version des Responses für http://.../foo?param=bar erstellt und damit die zwischengespeicherte Version überschrieben, jedoch nie ausgegeben, da die aufgerufene URL eine andere ist.

Für jeden zwischengespeicherten Response wird eine Cache-Datei unter var/tmp/n2n/responsecache abgelegt. Darum ist es wichtig, dass du alle Parameter validierst, die für den Response-Cache berücksichtigt werden. Im oben stehenden Beispiel könnte ein Angreifer doFoo() mit einem beliebigen Parameter "param" aufrufen und mit jedem unterschiedlichen Wert eine neue Cache-Datei erstellen und somit den Speicherplatz des Servers füllen.

Response Cache Store

Gehen wir davon aus, wir haben den Response einer Seite zwischengespeichert, die beliebige Daten aus einer Datenbank anzeigt. Ändern sich diese Daten, sind die Änderungen erst ersichtlich, wenn der zwischengespeicherte Response das nächste Mal erneuert werden muss. Damit du nicht warten musst, gibt dir der ResponseCacheStore die Möglichkeit, den Response Cache direkt nach der Änderung der Daten zu löschen. ResponseCacheStore ist lookupable und kann jeder magischen Methode übergeben werden.

Bei solchen Daten kann es sich zum Beispiel um Artikel handeln.

class BlogArticleDao implements RequestScoped {
    private $em;
    private $responseCacheStore;
    
    private function _init(EntityManager $em, ResponseCacheStore $responseCacheStore) {
        $this->em = $em;
        $this->responseCacheStore = $responseCacheStore;
    }

    public function saveArticle(BlogArticle $article) {
        $this->em->persist($article);
        $this->responseCacheStore->clear();
    }
}

Im oben stehenden Beispiel haben wir ein BlogArticleDao mit einer Methode saveArticle() implementiert, welche einen Artikel persistiert. Gleich nach dem Aufruf von EntityManger::persist() löschen wir über ResponseCacheStore::clear() den gesamten Response Cache deiner Applikation. Das bedeutet, dass alle zwischengespeicherten Responses gelöscht werden und beim nächsten Aufruf der jeweiligen Seite neu generiert werden.

In den meisten Fällen macht es mehr Sinn, das Löschen des Zwischenspeichers aus Lifecylce Callbacks anzustossen. So stellst du sicher, dass der Cache auch immer gelöscht wird, sobald ein Entity-Objekt (Datensatz) verändert wurde - dies zum Beispiel auch, wenn ein Nutzer über das Rocket diese Änderungen vornimmt.

class BlogArticle extends ObjectAdapter {

    // class body
    
    private function _postPersist(ResponseCacheStore $responseCacheStore) {
        $responseCacheStore->clear();
    }
    
    private function _postRemove(ResponseCacheStore $responseCacheStore) {
        $responseCacheStore->clear();
    }
    
    private function _postUpdate(ResponseCacheStore $responseCacheStore) {
        $responseCacheStore->clear();
    }
}

Im oben stehenden Beispiel wird der gesamte Response Cache gelöscht, sobald ein Artikel persistiert, gelöscht oder verändert wurde.

Damit du nicht für alle deine Entities diese drei Methoden implementieren musst, kannst du stattdessen einfach den Entity Listener n2n/web/http/orm/ResponseCacheClearer annotieren.

class BlogArticle extends ObjectAdapter {
    private static function _annos(AnnoInit $ai) {
        $ai->c(new AnnoEntityListeners(ResponseCacheClearer::getClass()));
        
        // further annotations
    }
    
    // rest of class body
    
}

Im oben stehenden Beispiel wird jetzt wie im Beispiel davor der gesamte Response Cache gelöscht, sobald ein Artikel persistiert, gelöscht oder verändert wurde.

Der ResponseCacheStore ist transaktional. Ist eine n2n-Transaktion aktiv, wird der Cache erst beim nächsten Commit effektiv gelöscht. Ist keine n2n-Transaktion aktiv, wird er sofort gelöscht.

Charakteristiken

Im vorherigen Abschnitt wurde dir gezeigt, wie du den gesamten Response Cache deiner Applikation löschen kannst. Der ResponseCacheStore bietet dir aber auch die Möglichkeit, den Response Cache selektiver zu löschen. Dazu musst du diesen aber zuerst kategorisieren. Dies erlaubt dir der dritte Parameter von ControllerAdapter::assignResponseCacheControl(), über welchen du sogenannte Charakteristiken bestimmen kannst. Eine Charakteristik besteht immer aus einem Schlüssel und einem Wert.

    public function doArticle(ArticleDao $articleDao, $id) {
        $article = $articleDao->getArticleById($id);
        if ($article === null) {
            throw new PageNotFoundException();
        }
        
        $this->assignResponseCacheControl(new \DateInterval('P1D'), false,
                array('type' => 'blogArticle', 'id' => $id));
        
        $this->forward('view\article.html', array('article' => $article));
    }

Die Controller-Methode doArticle() im oben stehenden Beispiel generiert den Response des Artikels der übergebenen $id. Dieser wird zwischengespeichert und mit den zwei Charakteristiken "type" und "id" ausgezeichnet. Dies erlaubt es uns, diesen zwischengespeicherten Response im Response Cache zu identifizieren und bei allfälligen Änderungen gezielt über ResponseCacheStore::removeByCharacteristics() zu löschen, ohne den Rest des Response Caches zu beeinflussen.

$responseCacheStore->removeByCharacteristics(array('type' => 'blogArticle', 'id' => $id));

Der oben stehende Methoden-Aufruf löscht den zwischengespeicherten Response eines Artikels.  

ResponseCacheStore::removeByCharacteristics() löscht die zwischengespeicherten Responses, die mit allen übergebenen Charakteristiken ausgezeichnet wurden. Die zwischengespeicherten Responses aller Artikel könntest du also mit folgendem Aufruf löschen:

$responseCacheStore->removeByCharacteristics(array('type' => 'blogArticle'));
« Caching HTTP-Cache »

Kommentare

Du musst eingeloggt sein, damit du Beiträge erstellen kannst.

Fragen