تقدم العديد من التطبيقات نفس المحتوى لجميع المستخدمين عند تحميل الصفحة الأولى. على سبيل المثال ، قد يعرض موقع إخباري آخر الأخبار ، أو قد يعرض موقع التجارة الإلكترونية العناصر الأكثر مبيعًا.
إذا تم تقديم هذا المحتوى من 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
. من خلال هذا التكوين ، سيخدم Firebase Hosting CDN محتوى الحزمة وفقًا لإعدادات ذاكرة التخزين المؤقت التي تم تعيينها بواسطة وظيفة السحابة. عند انتهاء صلاحية ذاكرة التخزين المؤقت ، سيتم تحديث المحتوى عن طريق تشغيل الوظيفة مرة أخرى.
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
// ...
}
المدخرات المقدرة
ضع في اعتبارك موقعًا إخباريًا يحصل على 100000 مستخدم يوميًا ويقوم كل مستخدم بتحميل نفس أهم 50 خبرًا عند التحميل الأولي. بدون أي تخزين مؤقت ، سيؤدي ذلك إلى 50 × 100،000 = 5،000،000 قراءة مستند يوميًا من Cloud Firestore.
افترض الآن أن الموقع يتبنى التقنية المذكورة أعلاه ويخزن تلك النتائج الخمسين مؤقتًا لمدة تصل إلى 5 دقائق. لذلك بدلاً من تحميل نتائج الاستعلام لكل مستخدم ، يتم تحميل النتائج بالضبط 12 مرة في الساعة. بغض النظر عن عدد المستخدمين الذين يصلون إلى الموقع ، فإن عدد الاستعلامات إلى Cloud Firestore يظل كما هو. بدلاً من قراءة 5.000.000 مستند ، ستستخدم هذه الصفحة 12 × 24 × 50 = 14400 قراءة مستند يوميًا. يمكن تعويض التكاليف الإضافية الصغيرة لاستضافة Firebase والوظائف السحابية بسهولة عن طريق توفير تكلفة Cloud Firestore.
بينما يستفيد المطور من توفير التكاليف ، يكون المستفيد الأكبر هو المستخدم. يمكن أن يؤدي تحميل هذه المستندات الخمسين من Firebase Hosting CDN بدلاً من Cloud Firestore مباشرةً إلى إزالة 100-200 مللي ثانية أو أكثر من وقت تحميل المحتوى للصفحة. أظهرت الدراسات مرارًا وتكرارًا أن الصفحات السريعة تعني مستخدمين أكثر سعادة.