【问题标题】:Can Firebase Hosting Serve Cached Data from Cloud Functions?Firebase 托管可以从云函数中提供缓存数据吗?
【发布时间】:2018-05-29 00:52:19
【问题描述】:

假设我在 Firestore 中有一个包含 100,000 条内容的数据库。每条内容每月更改一次以上的可能性不大。我的单页应用程序使用 firebase 托管,使用一个函数从 firestore 检索内容,将其呈现为 HTML,并将其返回给浏览器。

如果我经常为非动态的内容执行此过程,那会浪费我的 Firestore 配额并开始增加很多钱。动态

如何将该内容保存为静态 .com/path/path/contentpage.html 文件,以便在请求确切路径和查询时提供服务,而不是通过 firestore /函数每次处理?

我的目标是提高速度并减少不必要的 Firestore 请求,因为我知道每次读取都需要花钱。

谢谢!

【问题讨论】:

    标签: firebase caching google-cloud-functions google-cloud-firestore firebase-hosting


    【解决方案1】:

    当您在 Cloud Functions for Firebase 之上使用 Firebase Hosting 时,主机可以充当 HTTPS 函数响应之上的边缘缓存层。您可以在documentation 中阅读有关该集成的信息。特别是阅读managing cache behavior部分:

    您将用来管理缓存的主要工具是 Cache-Control 标头。 通过设置它,您可以与浏览器和 CDN 进行通信 你的内容应该被缓存。在您的功能中,您设置 像这样的缓存控制:

    res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
    

    【讨论】:

    • 如果我们在云运行中使用它怎么做?在 index.js 中间件中?
    • 当我使用这个时,帐单将如何计算?我不会因为向用户提供缓存内容而被收费吗?
    • @dshukertjr 是的。来自文档:“您还可以潜在地降低函数执行成本,因为内容是从 CDN 提供的,而不是通过触发函数提供的”。来源:firebase.google.com/docs/hosting/manage-cache
    【解决方案2】:

    除了设置 Cache-Control 标头之外,您还可以利用在 Cloud Functions 实例中设置全局变量的好处,请参阅 Cloud Functions Tips 其中提到“使用全局变量在未来的调用中重用对象 em>”。

    有了这个想法,我可以使用npm package treasury(是的,我确实开发了这个,但这与它恰好在 Cloud Functions 中使用这个用例这一事实无关——如果它的话,我也会在生产中使用它让你感觉更好)。

    只要变量treasury 存在,就会利用Treasury 的“内存”适配器来存储数据的示例,该变量与Cloud Function 实例一起存在和死亡:

    const functions = require('firebase-functions');
    const tauist = require('tauist');
    const Treasury = require('treasury');
    const cors = require('cors')({
        origin: true
    });
    
    // note that memory adapter uses MS not S for ttl
    const treasury = new Treasury({
        ttl: tauist.ms.thirtyMinutes
    });
    
    function getAnimalData({name}) {
        // replace this with your actual Promise code doing the "real work" that you want to cache
        return new Promise();
    }
    
    exports.animal = functions.https.onRequest((req, res) => {
        if (req.method !== 'GET') {
            return res.status(403).send('Forbidden');
        }
    
        // Enable CORS using the `cors` express middleware.
        return cors(req, res, () => {
            // Reading ticker symbol from URL query parameter.
            let name = (req.query.name || '').trim();
            console.log('animal name:', name);
    
            if (!name) {
                return res.status(400).send('Bad Request');
            }
    
            res.set('Cache-Control', `public, max-age=${tauist.s.thirtyMinutes}, s-maxage=${tauist.s.thirtyMinutes}`);
            treasury.invest(getAnimalData, {name})
                .then((data) => res.status(200).send(data))
                .catch((error) => {
                    console.error('error caught:', error);
                    res.status(500).send('Internal Server Error');
                });
    
        });
    });
    

    【讨论】:

      【解决方案3】:

      这是JAMstack 风格架构的一个很好的用例,您可以在其中预渲染页面,并在构建时加载您需要的数据。您可以将预渲染的静态站点构建器视为另一种形式的缓存。在这种情况下,考虑到您希望每月更新一次,在运行时服务器渲染页面甚至根本没有多大意义。

      当您的数据发生变化时,只需重建您的网站。 Gastby(在 React 世界中)是为此设置的,并且有许多不同的数据源可以插入到构建器中,包括用于 firestore 的插件。

      Netlify 是一个静态站点主机,它具有用于触发重建的电子书。您可以使用由事件触发的 Firebase 云功能,用于 ping Netlify 以在插入/更新/删除时运行“构建”的各种 firestore 集合/文档。

      这不仅更便宜,而且比运行运行时服务器更简单,并且由于静态页面加载速度最快,因此可以提供最高的最终用户性能。

      【讨论】:

      • 盖茨比是 ? 被高度低估的科技
      【解决方案4】:

      如果您需要缓存一些非常昂贵的结果或者可能来自另一个 API,您可以使用 fireStore 作为“提供者”。我的操作从约 30 秒到约 1 秒。您将减少出站配额的使用量

      这是我在服务中创建方法的示例代码:

                   ...
          import {db} from "../database";
          const tauist = require('tauist');
      
          ... 
      
          /**
           * @param name must be uniq
           * @param fallback
           * @param parameters
           * @param namespace
           * @param ttl milliseconds
           */
          static async getCache(name: string, fallback: (...a: any) => Promise<any>, parameters: any[], ttl: number = tauist.s.thirtyDays, namespace: string = 'api') {
              let response = {};
              const parms = parameters || [];
              const now = new Date();
              const collectionRef = db.collection(`cache-${namespace}-${parms.join('-')}`);
              const documentRef = collectionRef.doc(name);
              try {
                  const cacheSnapshot = await documentRef.get();
                  if (cacheSnapshot.exists) {
                      const cache = cacheSnapshot.data();
                      if (new Date(new Date((cache as Cache).created).getTime() + ttl) < now) {
                          throw new Error();
                      } else {
                          response = (cache as Cache).data;
                      }
                      // Using cache
                  } else {
                      throw new Error();
                  }
              } catch (e) {
                  // Cache created
                  response = await fallback(...parms);
                  await documentRef.set({
                      data: response,
                      created: new Date(new Date().getTime() + ttl)
                  })
              }
      
              return response;
          }

      我如何使用它:

      import {INTERNAL_SERVER_ERROR, OK, UNPROCESSABLE_ENTITY} from "http-status-codes";
      ...
      Service.getCache('getHugeData',  AWSService.getHugeData, [name, simple])
                      .then((data: any) => {
                          res.status(OK).json({
                              data
                          });
                      })
                      .catch((error: any) => {
                          console.log(error);
                          res.status(UNPROCESSABLE_ENTITY).json(error);
                      });
      

      【讨论】:

        猜你喜欢
        • 2020-04-19
        • 2018-07-22
        • 2019-06-01
        • 2018-08-18
        • 2013-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-08
        相关资源
        最近更新 更多