Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Подача связанного содержимого Firestore из CDN

Многие приложения предоставляют одинаковый контент всем пользователям при загрузке первой страницы. Например, новостной сайт может показывать последние новости, а сайт электронной коммерции может показывать самые продаваемые товары.

Если этот контент предоставляется из Cloud Firestore, каждый пользователь будет выдавать новый запрос для тех же результатов при загрузке приложения. Поскольку эти результаты не кэшируются между пользователями, приложение работает медленнее и дороже, чем должно быть.

Решение: Связки

Пакеты Cloud Firestore позволяют собирать пакеты данных из общих результатов запросов на бэкэнде с помощью Firebase Admin SDK и обслуживать эти предварительно вычисленные большие двоичные объекты, кэшированные в CDN. Это дает вашим пользователям гораздо более быструю первую загрузку и снижает затраты на запросы Cloud Firestore.

В этом руководстве мы будем использовать облачные функции для создания пакетов и хостинг Firebase для динамического кэширования и обслуживания содержимого пакетов. Более подробная информация о пучках доступна в руководствах .

Сначала создайте простую общедоступную функцию HTTP для запроса 50 последних «историй» и предоставления результата в виде пакета.

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);
});
      
Джава

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

Далее настроить Firebase хостинг для обслуживания и кэшировать это Облако функцию путем изменения firebase.json . С этой конфигурацией CDN хостинга Firebase будет обслуживать пакетный контент в соответствии с настройками кеша, установленными облачной функцией. Когда срок действия кеша истечет, он обновит содержимое, снова запустив функцию.

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

Наконец, в своем веб-приложении извлеките объединенный контент из CDN и загрузите его в 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
  // ...
}

Предполагаемая экономия

Рассмотрим новостной веб-сайт, который получает 100 000 пользователей в день, и каждый пользователь загружает одни и те же 50 главных новостей при начальной загрузке. Без кеширования это приведет к 50 x 100 000 = 5 000 000 операций чтения документов в день из Cloud Firestore.

Теперь предположим, что сайт применяет описанную выше технику и кэширует эти 50 результатов на срок до 5 минут. Таким образом, вместо загрузки результатов запроса для каждого пользователя результаты загружаются ровно 12 раз в час. Независимо от того, сколько пользователей приходит на сайт, количество запросов к Cloud Firestore остается неизменным. Вместо 5 000 000 чтений документа на этой странице будет использоваться 12 x 24 x 50 = 14 400 чтений документа в день. Небольшие дополнительные расходы на хостинг Firebase и облачные функции легко компенсируются экономией на Cloud Firestore.

В то время как разработчик получает выгоду от экономии затрат, больше всего выигрывает пользователь. Загрузка этих 50 документов из CDN хостинга Firebase, а не напрямую из Cloud Firestore, может легко сократить время загрузки содержимого страницы на 100-200 мс или более. Исследования неоднократно показывали, что быстрые страницы означают более счастливых пользователей.