Udostępniaj dołączoną zawartość Firestore z CDN

Wiele aplikacji udostępnia tę samą treść wszystkim użytkownikom przy pierwszym wczytaniu strony. Na przykład witryna z wiadomościami może pokazywać najnowsze artykuły, a witryna handlu elektronicznego może pokazywać najlepiej sprzedające się artykuły.

Jeśli ta treść jest udostępniana z Cloud Firestore, każdy użytkownik po załadowaniu aplikacji wyśle ​​nowe zapytanie o te same wyniki. Ponieważ wyniki te nie są buforowane między użytkownikami, aplikacja jest wolniejsza i droższa niż jest to konieczne.

Rozwiązanie: Pakiety

Pakiety Cloud Firestore umożliwiają łączenie pakietów danych na podstawie typowych wyników zapytań w backendzie za pomocą pakietu Firebase Admin SDK i udostępnianie tych wstępnie obliczonych obiektów BLOB zapisanych w pamięci podręcznej w sieci CDN. Zapewnia to użytkownikom znacznie szybsze pierwsze ładowanie i zmniejsza koszty zapytań Cloud Firestore.

W tym przewodniku użyjemy Cloud Functions do generowania pakietów, a Hostingu Firebase do dynamicznego buforowania i udostępniania zawartości pakietów. Więcej informacji o pakietach znajdziesz w poradnikach .

Najpierw utwórz prostą publiczną funkcję HTTP, która będzie wysyłać zapytania do 50 najnowszych „historii” i udostępniać wyniki jako pakiet.

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);
});
      
Jawa

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()));
  }
}
      

Następnie skonfiguruj Hosting Firebase do obsługi i buforowania tej funkcji w chmurze, modyfikując firebase.json . W tej konfiguracji sieć CDN Hostingu Firebase będzie obsługiwać zawartość pakietu zgodnie z ustawieniami pamięci podręcznej ustawionymi przez funkcję Cloud. Gdy pamięć podręczna wygaśnie, odświeży zawartość, ponownie uruchamiając funkcję.

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

Na koniec w swojej aplikacji internetowej pobierz dołączoną zawartość z CDN i załaduj ją do 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
  // ...
}

Szacunkowe oszczędności

Rozważ witrynę z wiadomościami, która ma 100 000 użytkowników dziennie, a każdy użytkownik ładuje te same 50 najważniejszych artykułów przy pierwszym wczytywaniu. Bez buforowania dałoby to 50 x 100 000 = 5 000 000 odczytów dokumentów dziennie z Cloud Firestore.

Teraz załóżmy, że witryna stosuje powyższą technikę i przechowuje te 50 wyników w pamięci podręcznej przez maksymalnie 5 minut. Więc zamiast wczytywać wyniki zapytania dla każdego użytkownika, wyniki są ładowane dokładnie 12 razy na godzinę. Bez względu na to, ilu użytkowników dociera do witryny, liczba zapytań do Cloud Firestore pozostaje taka sama. Zamiast 5 000 000 odczytów dokumentu, ta strona użyje 12 x 24 x 50 = 14 400 odczytów dokumentu dziennie. Niewielkie dodatkowe koszty hostingu Firebase i Cloud Functions można łatwo zrównoważyć dzięki oszczędnościom Cloud Firestore.

Podczas gdy deweloper korzysta z oszczędności kosztów, największym beneficjentem jest użytkownik. Ładowanie tych 50 dokumentów z CDN Hostingu Firebase zamiast bezpośrednio z Cloud Firestore może z łatwością skrócić czas ładowania zawartości strony o 100-200 ms lub więcej. Badania wielokrotnie wykazały, że szybkie strony oznaczają szczęśliwszych użytkowników.