获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Stellen Sie gebündelte Firestore-Inhalte von einem CDN bereit

Viele Anwendungen stellen allen Benutzern beim Laden der ersten Seite dieselben Inhalte bereit. Beispielsweise kann eine Nachrichtenseite die neuesten Geschichten zeigen, oder eine E-Commerce-Seite kann die meistverkauften Artikel zeigen.

Wenn dieser Inhalt von Cloud Firestore bereitgestellt wird, stellt jeder Benutzer beim Laden der Anwendung eine neue Abfrage für dieselben Ergebnisse. Da diese Ergebnisse nicht zwischen Benutzern zwischengespeichert werden, ist die Anwendung langsamer und teurer als nötig.

Lösung: Bündel

Mit Cloud Firestore-Paketen können Sie Datenpakete aus allgemeinen Abfrageergebnissen im Back-End mithilfe des Firebase Admin SDK zusammenstellen und diese vorberechneten Blobs bereitstellen, die in einem CDN zwischengespeichert sind. Dies bietet Ihren Benutzern ein viel schnelleres First-Load-Erlebnis und reduziert Ihre Cloud Firestore-Abfragekosten.

In diesem Leitfaden verwenden wir Cloud Functions zum Generieren von Bundles und Firebase Hosting zum dynamischen Zwischenspeichern und Bereitstellen von Bundle-Inhalten. Weitere Informationen zu Paketen finden Sie in den Leitfäden .

Erstellen Sie zunächst eine einfache öffentliche HTTP-Funktion, um die 50 neuesten „Geschichten“ abzufragen und das Ergebnis als Bündel bereitzustellen.

Node.js
exports.createBundle = functions.https.onRequest(async (request, response) => {
  // Query the 50 latest stories
  const latestStories = await db.collection('stories')
    .orderBy('timestamp', 'desc')
    .limit(50)
    .get();

  // Build the bundle from the query results
  const bundleBuffer = db.bundle('latest-stories')
    .add('latest-stories-query', latestStories)
    .build();

  // Cache the response for up to 5 minutes;
  // see https://firebase.google.com/docs/hosting/manage-cache
  response.set('Cache-Control', 'public, max-age=300, s-maxage=600');

  response.end(bundleBuffer);
});
      
Java

package com.example;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreBundle;
import com.google.cloud.firestore.Query.Direction;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.FirestoreClient;
import java.io.BufferedWriter;
import java.io.IOException;

public class ExampleFunction implements HttpFunction {

  public static FirebaseApp initializeFirebase() throws IOException {
    if (FirebaseApp.getApps().isEmpty()) {
      FirebaseOptions options = FirebaseOptions.builder()
          .setCredentials(GoogleCredentials.getApplicationDefault())
          .setProjectId("YOUR-PROJECT-ID")
          .build();

      FirebaseApp.initializeApp(options);
    }

    return FirebaseApp.getInstance();
  }

  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    // Get a Firestore instance
    FirebaseApp app = initializeFirebase();
    Firestore db = FirestoreClient.getFirestore(app);

    // Query the 50 latest stories
    QuerySnapshot latestStories = db.collection("stories")
        .orderBy("timestamp", Direction.DESCENDING)
        .limit(50)
        .get()
        .get();

    // Build the bundle from the query results
    FirestoreBundle bundle = db.bundleBuilder("latest-stores")
        .add("latest-stories-query", latestStories)
        .build();

    // Cache the response for up to 5 minutes
    // see https://firebase.google.com/docs/hosting/manage-cache
    response.appendHeader("Cache-Control", "public, max-age=300, s-maxage=600");

    // Write the bundle to the HTTP response
    BufferedWriter writer = response.getWriter();
    writer.write(new String(bundle.toByteBuffer().array()));
  }
}
      

Konfigurieren Sie als Nächstes Firebase Hosting so, dass diese Cloud-Funktion bereitgestellt und zwischengespeichert wird, indem Sie firebase.json . Mit dieser Konfiguration stellt das Firebase-Hosting-CDN den Bundle-Inhalt gemäß den von der Cloud-Funktion festgelegten Cache-Einstellungen bereit. Wenn der Cache abläuft, wird der Inhalt aktualisiert, indem die Funktion erneut ausgelöst wird.

firebase.json
{
  "hosting": {
    // ...
    "rewrites": [{
      "source": "/createBundle",
      "function": "createBundle"
    }]
  },
  // ...
}

Holen Sie schließlich in Ihrer Webanwendung den gebündelten Inhalt aus dem CDN und laden Sie ihn in das Firestore SDK.

// If you are using module bundlers.
import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/firestore/bundle" // This line enables bundle loading as a side effect.

async function fetchFromBundle() {
  // Fetch the bundle from Firebase Hosting, if the CDN cache is hit the 'X-Cache'
  // response header will be set to 'HIT'
  const resp = await fetch('/createBundle');

  // Load the bundle contents into the Firestore SDK
  await db.loadBundle(resp.body);

  // Query the results from the cache
  // Note: omitting "source: cache" will query the Firestore backend.
  
  const query = await db.namedQuery('latest-stories-query');
  const storiesSnap = await query.get({ source: 'cache' });

  // Use the results
  // ...
}

Geschätzte Einsparungen

Stellen Sie sich eine Nachrichten-Website vor, die 100.000 Benutzer pro Tag hat und jeder Benutzer beim ersten Laden die gleichen 50 Schlagzeilen lädt. Ohne Caching würde dies zu 50 x 100.000 = 5.000.000 Dokumentenlesevorgängen pro Tag aus Cloud Firestore führen.

Nehmen wir nun an, die Website übernimmt die obige Technik und speichert diese 50 Ergebnisse für bis zu 5 Minuten. Anstatt also die Abfrageergebnisse für jeden Benutzer zu laden, werden die Ergebnisse genau 12 Mal pro Stunde geladen. Unabhängig davon, wie viele Benutzer auf die Website gelangen, bleibt die Anzahl der Abfragen an Cloud Firestore gleich. Anstelle von 5.000.000 Dokumentlesevorgängen würde diese Seite 12 x 24 x 50 = 14.400 Dokumentlesevorgänge pro Tag verwenden. Die geringen Mehrkosten für Firebase Hosting und Cloud Functions werden durch die Kostenersparnis von Cloud Firestore problemlos kompensiert.

Während der Entwickler von den Kosteneinsparungen profitiert, ist der größte Nutznießer der Benutzer. Das Laden dieser 50 Dokumente aus dem Firebase-Hosting-CDN statt direkt aus Cloud Firestore kann die Ladezeit für Inhalte der Seite leicht um 100 bis 200 ms oder mehr verkürzen. Studien haben wiederholt gezeigt, dass schnelle Seiten zufriedenere Benutzer bedeuten.