Cache

Middleware, das Caching-Funktionalität bereitstellt.

Das Cache-Middleware kann den StatusCode, die Headers und den Body einer Response zwischenspeichern. Für bereits zwischengespeicherte Inhalte sendet das Cache-Middleware bei der Verarbeitung nachfolgender Anfragen den gepufferten Inhalt direkt aus dem Speicher an den Client.

Hinweis: Dieses Plugin speichert keine Response-Objekte, deren Body ResBody::Stream ist. Wenn es auf eine solche Response angewendet wird, verarbeitet Cache diese Anfragen nicht und es tritt kein Fehler auf.

Hauptfunktionen

  • CacheIssuer bietet eine Abstraktion zur Erzeugung von Cache-Schlüsseln. RequestIssuer ist eine seiner Implementierungen, mit der Sie definieren können, welche Teile der Anfrage-URL und der Anfrage-Method zur Erzeugung des Cache-Schlüssels verwendet werden sollen. Sie können auch Ihre eigene Logik zur Erzeugung von Cache-Schlüsseln definieren. Der Cache-Schlüssel muss nicht unbedingt eine Zeichenkette sein; jeder Typ, der die Einschränkungen Hash + Eq + Send + Sync + 'static erfüllt, kann als Schlüssel verwendet werden.

  • CacheStore bietet Operationen zum Speichern und Abrufen von Daten. MokaStore ist eine eingebaute, speicherbasierte Cache-Implementierung auf Basis von moka. Sie können auch Ihre eigene Implementierung definieren.

  • Cache ist eine Struktur, die Handler implementiert. Sie enthält auch ein internes skipper-Feld, mit dem Anfragen angegeben werden können, die das Caching überspringen sollen. Standardmäßig wird MethodSkipper verwendet, um alle Anfragen außer denen mit Method::GET zu überspringen.

    Beispiel für internen Implementierungscode:

    impl<S, I> Cache<S, I> {
      pub fn new(store: S, issuer: I) -> Self {
          let skipper = MethodSkipper::new().skip_all().skip_get(false);
          Cache {
              store,
              issuer,
              skipper: Box::new(skipper),
          }
      }
    }

Schnelle Migration von anderen Frameworks

Wenn Sie Caching-Mechanismen in anderen Frameworks verwendet haben, helfen Ihnen die folgenden konzeptionellen Zuordnungen, sich schneller an die Cache-Implementierung von Salvo anzupassen:

Migrationsleitfaden für Rust-Frameworks

  • Migration von Actix-web: Plugins wie actix-web-cache in Actix-web müssen in der Regel separat eingeführt werden, während Caching in Salvo Teil der Kernbibliothek ist.

    // Actix-web Cache-Beispiel
    use actix_web_cache::Cache;
    App::new().wrap(Cache::new().ttl(30))
    
    // Entsprechende Salvo-Implementierung
    use salvo::prelude::*;
    Router::new().hoop(Cache::new(MokaStore::new(100), RequestIssuer::new()))

Migrationsleitfaden für Frameworks in anderen Sprachen

  • Migration von Go/Gin: Gin verwendet ein Middleware-Muster, das Salvo auf ähnliche Weise übernimmt:

    // Gin Cache-Beispiel
    store := persist.NewMemoryStore(time.Second * 60)
    router.Use(cache.CachePage(store, time.Second * 30))
    // Entsprechende Salvo-Implementierung
    let store = MokaStore::new(100).with_ttl(Duration::from_secs(30));
    router.hoop(Cache::new(store, RequestIssuer::new()))
  • Migration von Spring Boot: Das deklarative Caching von Spring Boot muss in die explizite Middleware-Konfiguration von Salvo umgewandelt werden:

    // Spring Boot
    @Cacheable(value = "books", key = "#isbn")
    public Book findBook(ISBN isbn) { ... }
    // Entsprechende Salvo-Implementierung - Anwendung des Caches auf Routenebene
    let custom_issuer = YourCustomIssuer::new(); // Implementiert die CacheIssuer-Schnittstelle
    Router::with_path("books").hoop(Cache::new(MokaStore::new(100), custom_issuer))
  • Migration von Express.js: Das Caching-Middleware von Express.js ist konzeptionell ähnlich wie das von Salvo, aber die Syntax unterscheidet sich:

    // Express.js
    const apicache = require('apicache');
    app.use(apicache.middleware('5 minutes'));
    
    // Entsprechende Salvo-Implementierung
    let store = MokaStore::new(100).with_ttl(Duration::from_secs(300));
    router.hoop(Cache::new(store, RequestIssuer::new()))

Bei der Migration von anderen Frameworks sollten Sie auf mehrere Schlüsselkonzepte des Caching in Salvo achten:

  1. Cache-Schlüsselgenerierung - Gesteuert über die CacheIssuer-Schnittstelle.
  2. Cache-Speicher - Implementiert über die CacheStore-Schnittstelle.
  3. Logik zum Überspringen des Caches - Angepasst über den skipper-Mechanismus.

Standardmäßig speichert Salvo nur GET-Anfragen zwischen, was mit dem Standardverhalten der meisten Frameworks übereinstimmt.

Beispielcode

main.rs
Cargo.toml
use std::time::Duration;

use salvo::cache::{Cache, MokaStore, RequestIssuer};
use salvo::prelude::*;
use salvo::writing::Text;
use time::OffsetDateTime;

// Handler for serving the home page with HTML content
#[handler]
async fn home() -> Text<&'static str> {
    Text::Html(HOME_HTML)
}

// Handler for short-lived cached response (5 seconds)
#[handler]
async fn short() -> String {
    format!(
        "Hello World, my birth time is {}",
        OffsetDateTime::now_utc()
    )
}

// Handler for long-lived cached response (1 minute)
#[handler]
async fn long() -> String {
    format!(
        "Hello World, my birth time is {}",
        OffsetDateTime::now_utc()
    )
}

#[tokio::main]
async fn main() {
    // Initialize logging system
    tracing_subscriber::fmt().init();

    // Create cache middleware for short-lived responses (5 seconds TTL)
    let short_cache = Cache::new(
        MokaStore::builder()
            .time_to_live(Duration::from_secs(5))
            .build(),
        RequestIssuer::default(),
    );

    // Create cache middleware for long-lived responses (60 seconds TTL)
    let long_cache = Cache::new(
        MokaStore::builder()
            .time_to_live(Duration::from_secs(60))
            .build(),
        RequestIssuer::default(),
    );

    // Set up router with three endpoints:
    // - / : Home page
    // - /short : Response cached for 5 seconds
    // - /long : Response cached for 1 minute
    let router = Router::new()
        .get(home)
        .push(Router::with_path("short").hoop(short_cache).get(short))
        .push(Router::with_path("long").hoop(long_cache).get(long));

    // Bind server to port 8698 and start serving
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    Server::new(acceptor).serve(router).await;
}

// HTML template for the home page with links to cached endpoints
static HOME_HTML: &str = r#"
<!DOCTYPE html>
<html>
    <head>
        <title>Cache Example</title>
    </head>
    <body>
        <h2>Cache Example</h2>
        <p>
            This examples shows how to use cache middleware.
        </p>
        <p>
            <a href="/short" target="_blank">Cache 5 seconds</a>
        </p>
        <p>
            <a href="/long" target="_blank">Cache 1 minute</a>
        </p>
    </body>
</html>
"#;