Phân phát nội dung đi kèm trên Firestore từ một CDN

Nhiều ứng dụng cung cấp cùng một nội dung cho tất cả người dùng khi tải trang lần đầu. Ví dụ: một trang tin tức có thể hiển thị các tin bài mới nhất hoặc một trang thương mại điện tử có thể hiển thị các mặt hàng bán chạy nhất.

Nếu nội dung này được cung cấp từ Cloud Firestore, thì mỗi người dùng sẽ đưa ra một truy vấn mới cho cùng một kết quả khi họ tải ứng dụng. Vì các kết quả này không được lưu vào bộ nhớ đệm giữa những người dùng, nên ứng dụng sẽ chậm hơn và tốn kém hơn mức cần thiết.

Giải pháp: Gói

Gói Cloud Firestore cho phép bạn tập hợp các gói dữ liệu từ kết quả truy vấn phổ biến trên phần phụ trợ bằng SDK của Firebase dành cho quản trị viên và cung cấp các blob được tính toán trước này được lưu vào bộ nhớ đệm trên CDN. Điều này giúp người dùng có trải nghiệm tải lần đầu nhanh hơn nhiều và giảm chi phí truy vấn Cloud Firestore.

Trong hướng dẫn này, chúng ta sẽ sử dụng Cloud Functions để tạo gói và Firebase Hosting để lưu vào bộ nhớ đệm một cách linh động và cung cấp nội dung gói. Bạn có thể xem thêm thông tin về gói trong các hướng dẫn.

Trước tiên, hãy tạo một hàm HTTP công khai đơn giản để truy vấn 50 "tin bài" mới nhất và cung cấp kết quả dưới dạng gói.

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

Tiếp theo, hãy định cấu hình Lưu trữ Firebase để cung cấp và lưu vào bộ nhớ đệm Cloud Function này bằng cách sửa đổi firebase.json. Với cấu hình này, CDN Firebase Hosting sẽ cung cấp nội dung gói theo chế độ cài đặt bộ nhớ đệm do Cloud Function đặt. Khi bộ nhớ đệm hết hạn, hệ thống sẽ làm mới nội dung bằng cách kích hoạt lại hàm.

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

Cuối cùng, trong ứng dụng web, hãy tìm nạp nội dung được gói từ CDN và tải nội dung đó vào 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
  // ...
}

Mức tiết kiệm theo ước tính

Hãy xem xét một trang web tin tức có 100.000 người dùng mỗi ngày và mỗi người dùng tải cùng 50 tin bài hàng đầu khi tải lần đầu. Nếu không có bộ nhớ đệm, điều này sẽ dẫn đến 50 x 100.000 = 5.000.000 lượt đọc tài liệu mỗi ngày từ Cloud Firestore.

Giả sử trang web áp dụng kỹ thuật ở trên và lưu vào bộ nhớ đệm 50 kết quả đó trong tối đa 5 phút. Vì vậy, thay vì tải kết quả truy vấn cho mọi người dùng, kết quả sẽ được tải chính xác 12 lần mỗi giờ. Bất kể có bao nhiêu người dùng truy cập vào trang web, số lượng truy vấn đến Cloud Firestore vẫn giữ nguyên. Thay vì 5.000.000 lượt đọc tài liệu, trang này sẽ sử dụng 12 x 24 x 50 = 14.400 lượt đọc tài liệu mỗi ngày. Các chi phí bổ sung nhỏ cho Lưu trữ Firebase và Cloud Functions dễ dàng được bù đắp bằng mức tiết kiệm Cloud Firestore chi phí.

Mặc dù nhà phát triển được hưởng lợi từ việc tiết kiệm chi phí, nhưng người dùng là đối tượng hưởng lợi nhiều nhất. Việc tải 50 tài liệu này từ CDN Lưu trữ Firebase thay vì từ Cloud Firestore trực tiếp có thể dễ dàng giảm 100 – 200 mili giây hoặc nhiều hơn thời gian tải nội dung của trang. Các nghiên cứu đã nhiều lần cho thấy rằng các trang nhanh chóng có nghĩa là người dùng hài lòng hơn.