Gebündelte Firestore-Inhalte aus einem CDN bereitstellen

Viele Anwendungen liefern beim ersten Seitenaufbau allen Nutzern dieselben Inhalte. Für z. B. auf einer Nachrichtenwebsite die neuesten Meldungen oder auf einer E-Commerce-Website meistverkauften Artikel.

Wenn dieser Inhalt über Cloud Firestore bereitgestellt wird, gibt jeder Nutzer ein neues wenn sie die Anwendung laden. 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 Daten-Bundles aus einer gemeinsamen Abfrage zusammenstellen mit dem Firebase Admin SDK im Back-End ausgeben und diese vorab berechnete Blobs, die auf einem CDN im Cache gespeichert sind. Das gibt Ihren Nutzenden schnelleres erstmaliges Laden und senkt die Kosten für Cloud Firestore-Abfragen.

In dieser Anleitung verwenden wir Cloud Functions, um Bundles zu generieren, und Firebase Hosting, um Bundle-Inhalte dynamisch im Cache zu speichern und bereitzustellen. Mehr Informationen zu Paketen finden Sie in den Leitfäden.

Erstellen Sie zuerst eine einfache öffentliche HTTP-Funktion, um die letzten 50 „Meldungen“ abzufragen. und das Ergebnis als Bundle bereitstellen.

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. Mit dieser Konfiguration ist das Firebase Hosting-CDN wird der Bundle-Inhalt gemäß den Cache-Einstellungen des Cloud Functions-Funktion Wenn der Cache abläuft, wird der Inhalt durch Auslösen einer die Funktion wiederholen.

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

Rufen Sie schließlich in Ihrer Webanwendung den gebündelten Content aus dem CDN ab und laden Sie in das Firestore SDK ein.

// 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 für maximal 5 Minuten. Anstatt die Abfrageergebnisse für jeden Nutzer zu laden, werden sie also genau 12 Mal pro Stunde geladen. Ganz gleich, wie viele Nutzer auf der Website ist die Anzahl der Abfragen an Cloud Firestore gleich. Anstelle von 5.000.000 Lesevorgänge, für diese Seite wäre 12 x 24 x 50 = 14.400 Dokumente Lesevorgänge pro Tag. Die geringen Zusatzkosten für Firebase Hosting und Cloud Functions können leicht durch die Kosteneinsparungen von Cloud Firestore ausgeglichen werden.

Auch wenn der Entwickler von den Kosteneinsparungen profitiert, ist der Nutzer der größte Nutznießer. Wenn Sie diese 50 Dokumente über das Firebase Hosting-CDN statt direkt über Cloud Firestore laden, lässt sich die Ladezeit der Inhalte der Seite um 100 bis 200 Millisekunden oder mehr verkürzen. Studien haben wiederholt gezeigt, dass schnelle Seiten für zufriedenere Nutzer.