عرض محتوى Firestore مجمّع من شبكة توصيل محتوى

تعرِض العديد من التطبيقات المحتوى نفسه لجميع المستخدمين عند تحميل الصفحة لأول مرة. بالنسبة على سبيل المثال، قد يعرض موقع أخبار آخر الأخبار أو قد يعرض موقع للتجارة الإلكترونية العناصر الأكثر مبيعًا.

إذا تم عرض هذا المحتوى من Cloud Firestore، سيُجري كل مستخدم طلب بحث جديدًا للحصول على النتائج نفسها عند تحميل التطبيق. لأن هذه لا يتم تخزين النتائج مؤقتًا بين المستخدمين، يكون التطبيق أبطأ وأكثر باهظ الثمن مما ينبغي.

الحل: الحِزم

Cloud Firestore تتيح لك حِزم البيانات تجميع حِزم البيانات من نتائج طلبات البحث المشترَكة في الخلفية باستخدام حزمة Firebase Admin SDK، وعرض هذه المجموعات المجمّعة المحفوظة مؤقتًا في ذاكرة التخزين المؤقت على شبكة توصيل المحتوى (CDN). يمنح ذلك المستخدمين فرصة تجربة تحميل أول بشكل أسرع وتقليل تكاليف طلبات البحث التي تبلغ 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);
});
      
جافا

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 محتوى الحِزمة وفقًا لإعدادات ذاكرة التخزين المؤقت التي تحدّدها وظيفة Cloud. عند انتهاء صلاحية ذاكرة التخزين المؤقت، ستتم إعادة تحميل المحتوى من خلال بدء الدالة مرة أخرى.

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

أخيرًا، في تطبيق الويب، يمكنك جلب المحتوى المجمّع من شبكة CDN وتحميله إلى حزمة تطوير البرامج (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
  // ...
}

نسبة التوفير المقدّرة

لنفترض أنّ هناك موقعًا إلكترونيًا إخباريًا يجذب 100,000 مستخدم في اليوم، ويحمّل كل مستخدم أهم 50 قصة في الصفحة الرئيسية نفسها عند التحميل الأولي. بدون أي تخزين مؤقت، سيؤدي ذلك إلى 50 × 100,000 = 5,000,000 قراءة للمستندات في اليوم من Cloud Firestore.

لنفترض الآن أنّ الموقع الإلكتروني يتّبع التقنية أعلاه ويخزّن هذه النتائج الـ 50 في ذاكرة التخزين المؤقت مدّةً تصل إلى 5 دقائق. لذا، بدلاً من تحميل نتائج طلبات البحث لكل مستخدم، يتم تحميل النتائج 12 مرة في الساعة بالضبط. بغض النظر عن عدد المستخدمين الذين يصلون في الموقع، سيظل عدد الاستعلامات إلى Cloud Firestore كما هو. بدلاً من 5,000,000 قراءات للمستند، ستستخدم هذه الصفحة 12 × 24 × 50 = 14,400 مستند قراءات في اليوم. يمكن تعويض التكاليف الإضافية البسيطة لميزة "استضافة Firebase" وCloud Functions بسهولة من خلال التوفير في تكاليف Cloud Firestore.

في حين يستفيد المطوّر من التوفير في التكلفة، يستفيد المستخدم أكثر من غيره. تحميل هذه المستندات الخمسين من شبكة توصيل المحتوى التي تستضيفها Firebase بدلاً من من Cloud Firestore مباشرةً حلاقة 100-200 ملّي ثانية أو أكثر من وقت تحميل محتوى الصفحة. وقد أظهرت الدراسات مرارًا أن الصفحات السريعة يعني مستخدمين أكثر سعادة.