Viele Anwendungen liefern beim ersten Seitenaufbau allen Nutzern dieselben Inhalte. Beispielsweise könnten auf einer Nachrichtenwebsite die neuesten Meldungen oder auf einer E-Commerce-Website die meistverkauften Artikel angezeigt werden.
Wenn dieser Inhalt von Cloud Firestore bereitgestellt wird, gibt jeder Nutzer beim Laden der Anwendung eine neue Abfrage für dieselben Ergebnisse aus. Da diese Ergebnisse nicht zwischen Nutzern im Cache gespeichert werden, ist die Anwendung langsamer und teurer als nötig.
Lösung: Bundles
Mit Cloud Firestore-Bundles können Sie Datenpakete aus gängigen Abfrageergebnissen im Backend mit dem Firebase Admin SDK zusammenstellen und diese vorab berechneten Blobs in einem CDN ausliefern. Dies beschleunigt den ersten Ladevorgang für Ihre Nutzer und senkt die Kosten für Cloud Firestore-Abfragen.
In diesem Leitfaden verwenden wir Cloud Functions, um Bundles zu generieren, und Firebase Hosting, um Bundle-Content dynamisch im Cache zu speichern und bereitzustellen. Weitere Informationen zu Paketen finden Sie in den Leitfäden.
Erstellen Sie zuerst eine einfache öffentliche HTTP-Funktion, um die 50 aktuellsten „Stories“ abzufragen und das Ergebnis als Bundle 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 im Cache gespeichert wird. Ändern Sie dazu firebase.json
. Bei dieser Konfiguration stellt das Firebase Hosting CDN die Bundle-Inhalte gemäß den Cache-Einstellungen bereit, die von der Cloud-Funktion festgelegt wurden. Wenn der Cache abläuft, werden die Inhalte aktualisiert, indem die Funktion noch einmal ausgelöst wird.
firebase.json
{
"hosting": {
// ...
"rewrites": [{
"source": "/createBundle",
"function": "createBundle"
}]
},
// ...
}
Rufen Sie schließlich in Ihrer Webanwendung den gebündelten Inhalt aus dem CDN ab 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 Einsparung
Angenommen, eine Nachrichtenwebsite wird von 100.000 Nutzern pro Tag besucht und jeder Nutzer lädt beim ersten Laden dieselben 50 Top-Artikel. Ohne Caching würde dies zu 50 × 100.000 = 5.000.000 Dokumentenlesevorgängen pro Tag von Cloud Firestore führen.
Nehmen wir nun an, dass die Website die oben beschriebene Technik anwendet und diese 50 Ergebnisse bis zu 5 Minuten lang im Cache speichert. Anstatt die Abfrageergebnisse für jeden Nutzer zu laden, werden sie also genau 12 Mal pro Stunde geladen. Unabhängig davon, wie viele Nutzer die Website besuchen, bleibt die Anzahl der Abfragen an Cloud Firestore gleich. Anstatt 5.000.000 Dokumentaufrufe würde für diese Seite 12 × 24 × 50 = 14.400 Dokumentaufrufe pro Tag verwendet. Die geringen Zusatzkosten für Firebase Hosting und Cloud Functions werden leicht durch die Kosteneinsparungen von Cloud Firestore ausgeglichen.
Auch wenn der Entwickler von den Kosteneinsparungen profitiert, ist der Nutzer der größte Nutznießer. Wenn Sie diese 50 Dokumente direkt aus dem Firebase Hosting CDN statt aus Cloud Firestore laden, kann dies die Ladezeit der Seite um 100 bis 200 ms oder mehr verkürzen. Studien haben wiederholt gezeigt, dass schnelle Seiten für zufriedenere Nutzer sorgen.