Firebase 函数:托管重写以动态生成具有 50000 多个链接的 sitemap.xml

问题描述

我想在用户或抓取工具请求 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 上按读/写付费,因此我建议您重新考虑您的架构。

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

第 1 步:Google Storage 而不是 Firestore

创建页面后,将该 URL 附加到 Google Storage 存储分区中单独一行的文本文件中。如果您的网址具有唯一 ID,您可以使用它来搜索和替换现有网址。

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

如果将 URL 分解为更小的文件可能会有所帮助。如果某些 URL 以 /foo 开头而其他 URL 以 /bar 开头,我将创建至少两个名为 sitemap_foo.txtsitemap_bar.txt 的文件,并将这些 URL 存储到各自的文件中。>

第 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 步:删除损坏的网址

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

总结

通过上述系统,您将拥有一个可扩展、可靠且高效的站点地图生成系统,该系统的操作成本可能很低。

回答您的问题

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

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

问:我每次添加新用户/帖子/页面时都需要更新站点地图吗?

答:是的。

问:如何在不发生冲突的情况下写入单个文本文件?

答: 冲突是可能的,但每秒创建多少新页面/帖子/用户?如果每秒超过一个,我将创建一个 Pub/Sub 主题,该主题具有一个功能,可以将其排空以批量更新站点地图。否则我只会让它直接更新。

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

答:根据您拥有的用户数量,将其进一步细分为每月/每周/每天的用户可能是明智之举。因此,您将拥有包含当天创建的所有用户的 sitemap_users_20200214.txt。这很可能会阻止 50,000 个网址的限制。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...