Firebase 函数:托管重写以动态生成 sitemap.xml 超过 50000 个链接

Firebase Functions: hosting rewrite to dynamically generate sitemap.xml with more than 50000 links

我想在用户或爬虫请求 https://www.example.com/sitemap.xml 时使用 Cloud Functions 动态生成包含所有静态和动态用户链接(通过来自 Firestore 的 uid)的 sitemap.xml。我已经设法使用 sitemap.js (https://github.com/ekalinin/sitemap.js#generate-a-one-time-sitemap-from-a-list-of-urls) 和 Firebase Hosting 重写实现了一个工作版本。但是,我当前的解决方案(见下文)生成一个大 sitemap.xml,并且最多只能用于 50000 个不可扩展的链接。

当前解法:

firebase.json 中重写托管:

  "hosting": [
      ...
      "rewrites": [
        {
          "source": "/sitemap.xml",
          "function": "generate_sitemap"
        },
      ]
    }
  ],

函数在index.ts

export const generateSitemap = functions.region('us-central1').https.onRequest((req, res) => {

  const afStore = admin.firestore();
  const promiseArray: Promise<any>[] = [];

  const stream = new SitemapStream({ hostname: 'https://www.example.com' });
  const fixedLinks: any[] = [
    { url: `/start/`, changefreq: 'hourly', priority: 1 },
    { url: `/help/`, changefreq: 'weekly', priority: 1 }
  ];

  const userLinks: any[] = [];

  promiseArray.push(afStore.collection('users').where('active', '==', true).get().then(querySnapshot => {
    querySnapshot.forEach(doc => {
      if (doc.exists) {
        userLinks.push({ url: `/user/${doc.id}`, changefreq: 'daily', priority: 1 });
      }
    });
  }));

  return Promise.all(promiseArray).then(() => {
    const array = fixedLinks.concat(userLinks);
    return streamToPromise(Readable.from(array).pipe(stream)).then((data: any) => {
      res.set('Content-Type', 'text/xml');
      res.status(200).send(data.toString());
      return;
    });
  });
});

因为这只能扩展到大约 50000 个链接,所以我想做一些类似 https://github.com/ekalinin/sitemap.js#create-sitemap-and-index-files-from-one-large-list 的事情。但似乎我需要以某种方式实际创建和临时存储 .xml 文件。

有人遇到过这个问题吗?

如您所述,这不可扩展,而且您的成本将会飙升,因为您在 Firestore 上按 read/write 付费,因此我建议您重新考虑您的架构。

几年前,我为 App Engine 网站解决了一个类似的问题,该网站需要为数百万个动态创建的页面生成站点地图,而且它非常高效,从未超过免费层的限制。

第 1 步:Google 存储而不是 Firestore

创建页面后,将 URL 附加到 Google 存储桶中的文本文件中。如果您的 URL 有一个唯一的 ID,您可以使用它来搜索和替换现有的 URL。

https://www.example.com/foo/some-long-title
https://www.example.com/bar/some-longer-title

If 可能有助于将 URL 分成更小的文件。如果一些 URL 以 /foo 开头,而另一些以 /bar 开头,我将至少创建两个名为 sitemap_foo.txtsitemap_bar.txt 的文件并存储 URLs 到各自的文件中。

第 2 步:动态生成站点地图索引

创建指向多个站点地图文件的 sitemap index,而不是普通的巨大 XML 站点地图。

当 /sitemap.xml 被访问时,通过循环访问存储桶中的站点地图文件并像这样列出它们来生成以下索引:

<?xml version="1.0" encoding="UTF-8"?>
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <sitemap>
      <loc>https://storage.google...../sitemap_foo.txt</loc>
    </sitemap>
    <sitemap>
      <loc>https://storage.google...../sitemap_bar.txt</loc>
    </sitemap>
  </sitemapindex>

步骤 3:删除损坏的 URLs

更新您的 404 控制器以搜索并从您的站点地图中删除 URL(如果找到)。

总结

使用上述系统,您将拥有一个可扩展、可靠且高效的站点地图生成系统,您的操作成本可能很少甚至为零。

问题的答案

问:站点地图中可以有多少个 URL?

A: According to Google, 50,000 或 50MB 未压缩。

问:我每次添加新的 user/post/page 时都需要更新站点地图吗?

答:是。

问:如何写入单个文本文件而不会发生冲突?

A: 可能会发生冲突,但是每秒会创建多少个新的 pages/posts/users?如果每秒超过一个,我会创建一个 Pub/Sub 主题,其功能是耗尽它以批量更新站点地图。否则我就让它直接更新。

问:假设我为所有用户创建了一个 sitemap_users.txt...

A: 根据您拥有的用户数量,明智的做法是将其进一步细分,以按 month/week/day 分组。因此,您将拥有 sitemap_users_20200214.txt,其中包含当天创建的所有用户。这很可能会阻止 50,000 URL 限制。