許多應用程序在第一頁加載時向所有用戶提供相同的內容。例如,新聞站點可能會顯示最新的故事,或者電子商務站點可能會顯示最暢銷的商品。
如果此內容是從 Cloud Firestore 提供的,則每個用戶在加載應用程序時都會針對相同的結果發出新的查詢。因為這些結果沒有在用戶之間緩存,所以應用程序比它需要的更慢且更昂貴。
解決方案:捆綁
Cloud Firestore 捆綁包允許您使用 Firebase Admin SDK 從後端的常見查詢結果組裝數據包,並提供緩存在 CDN 上的這些預先計算的 blob。這可為您的用戶帶來更快的首次加載體驗,並降低您的 Cloud Firestore 查詢成本。
在本指南中,我們將使用 Cloud Functions 生成捆綁包,並使用 Firebase 託管動態緩存和提供捆綁包內容。有關捆綁包的更多信息,請參閱指南。
首先創建一個簡單的公共 HTTP 函數來查詢 50 個最新的“故事”並將結果作為一個包提供。
節點.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.json
配置 Firebase 託管服務和緩存這個 Cloud Function。使用此配置,Firebase 託管 CDN 將根據 Cloud Function 設置的緩存設置提供捆綁包內容。當緩存過期時,它將通過再次觸發該函數來刷新內容。
firebase.json
{
"hosting": {
// ...
"rewrites": [{
"source": "/createBundle",
"function": "createBundle"
}]
},
// ...
}
最後,在您的 Web 應用程序中,從 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 個頭條新聞。如果沒有任何緩存,這將導致每天從 Cloud Firestore 讀取 50 x 100,000 = 5,000,000 次文檔。
現在假設站點採用上述技術並將這 50 個結果緩存最多 5 分鐘。因此,不是為每個用戶加載查詢結果,而是每小時加載 12 次結果。無論有多少用戶到達該站點,對 Cloud Firestore 的查詢數量都保持不變。此頁面每天將使用 12 x 24 x 50 = 14,400 次文檔讀取,而不是 5,000,000 次文檔讀取。 Firebase 託管和 Cloud Functions 的少量額外成本很容易被 Cloud Firestore 成本節省所抵消。
在開發者從成本節約中獲益的同時,最大的受益者是用戶。從 Firebase 託管 CDN 而不是直接從 Cloud Firestore 加載這 50 個文檔可以輕鬆地將頁面內容加載時間縮短 100-200 毫秒或更多。研究一再表明,快速頁面意味著更快樂的用戶。