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

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

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

الحل: الحِزم

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

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 قصة عند التحميل الأولي. بدون أي تخزين مؤقت، سيؤدي ذلك إلى 50 × 100,000 = 5,000,000 قراءة للمستندات في اليوم من Cloud Firestore.

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

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