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

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

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

Rozwiązanie: Pakiety

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

W tym przewodniku będziemy używać Cloud Functions do generowania pakietów i Firebase Hosting 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 prześle zapytanie do 50 najnowszych „historii” i udostępni wynik 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 chmury, modyfikując firebase.json . Przy tej konfiguracji sieć CDN Hostingu Firebase będzie udostępniać zawartość pakietu zgodnie z ustawieniami pamięci podręcznej ustawionymi przez funkcję Cloud. Po wygaśnięciu pamięci podręcznej 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 pakietu SDK Firestore.

// 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
  // ...
}

Szacowane oszczędności

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

Załóżmy teraz, że witryna stosuje powyższą technikę i buforuje te 50 wyników przez maksymalnie 5 minut. Zamiast więc ładować 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 pojawi się na stronie, liczba zapytań do Cloud Firestore pozostaje taka sama. Zamiast 5 000 000 odczytań dokumentów, ta strona wymagałaby 12 x 24 x 50 = 14 400 odczytów dokumentów dziennie. Niewielkie dodatkowe koszty hostingu Firebase i funkcji chmurowych można łatwo zrekompensować oszczędnościami w Cloud Firestore.

Choć deweloper czerpie korzyści z oszczędności, największym beneficjentem jest użytkownik. Załadowanie tych 50 dokumentów z Firebase Hosting CDN 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.