許多應用程式會在初次載入網頁時,為所有使用者提供相同的內容。舉例來說,新聞網站可能會顯示最新的新聞,而電子商務網站則可能會顯示暢銷商品。
如果這項內容是從 Cloud Firestore 提供,每位使用者在載入應用程式時,都會針對相同的結果發出新的查詢。由於這些結果不會在使用者之間快取,因此應用程式會變得較慢,且成本也較高。
解決方案:組合
Cloud Firestore 套件可讓您使用 Firebase Admin SDK,從後端的常見查詢結果組合資料套件,並提供在 CDN 快取的預先計算的 Blob。這麼做可為使用者提供更快速的首次載入體驗,並降低 Cloud Firestore 查詢成本。
在本指南中,我們會使用 Cloud Functions 產生組合,並使用 Firebase Hosting 動態快取及提供組合內容。如要進一步瞭解軟體包,請參閱指南。
首先,請建立簡單的公開 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); });
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())); } }
接著,修改 firebase.json
,設定 Firebase 代管服務,以便提供及快取這個 Cloud 函式。有了這個設定,Firebase Hosting CDN 就會根據 Cloud Function 設定的快取設定,提供套件內容。快取過期時,系統會再次觸發函式來重新整理內容。
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 則熱門新聞。如果沒有快取,這會導致 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 載入這 50 份文件,而非直接從 Cloud Firestore 載入,即可輕鬆縮短頁面內容載入時間 100 到 200 毫秒以上。研究一再證明,網頁載入速度越快,使用者就越滿意。