【问题标题】:Google App Engine - Caching generated HTMLGoogle App Engine - 缓存生成的 HTML
【发布时间】:2010-12-28 04:58:03
【问题描述】:

我编写了一个 Google App Engine 应用程序,它以编程方式生成一堆 HTML 代码,这些代码对于每个登录我的系统的用户来说都是相同的输出,我知道当代码运行时这将是低效的投入生产。所以,我试图找出缓存生成页面的最佳方法。

最可能的选择是生成页面并将它们写入数据库,然后根据上次更新代码的时间检查给定页面的数据库 put 操作时间。然后,如果代码比上次放入数据库的代码更新(对于特定的 HTML 请求),则将生成和提供新的 HTML,并缓存到数据库中。如果代码比上次放入数据库的代码更旧,那么我将直接从数据库中获取 HTML 并提供它(因此避免了生成 HTML 的所有 CPU 浪费)。我不仅希望尽量减少加载时间,还要尽量减少 CPU 使用率。

但是,我遇到的一个问题是,我不知道如何以编程方式检查上传到应用引擎的代码版本何时更新。

我愿意接受有关此方法或缓存生成的 html 的其他方法的任何建议。

请注意,虽然 memcache 可以在这种情况下提供帮助,但我认为这不是最终的解决方案,因为我真的只需要在代码更新时重新生成 html(而不是每次 memcache 过期)。

【问题讨论】:

  • 但是……这就是 memcache 的用途!除非生成 HTML 需要非常非常长的时间,否则你想多了。
  • 此外,App Engine 内存缓存模型似乎只缓存数据存储访问,而不是代码生成:来自:code.google.com/appengine/docs/python/memcache/…——内存缓存通常与以下模式一起使用:应用程序从用户或应用程序。应用程序检查满足该查询所需的数据是否在内存缓存中。如果数据在内存缓存中,则应用程序使用该数据。如果数据不在 memcache 中,应用程序会查询数据存储并将结果存储在 memcache 中以供将来请求。
  • @Alexander - 你可以在内存缓存中放任何你想要的东西,他们提到的是典型的用例。
  • 您好,谢谢。很高兴知道!
  • +1 - 好问题。我无法相信(不断地)对问题缺乏任何形式的支持。一个好的问题和一个好的答案一样有价值,因为问题通常是人们如何开始寻找答案......

标签: html google-app-engine caching


【解决方案1】:

按速度排序:

  1. 内存缓存
  2. 在数据存储中缓存 HTML
  3. 整页生成

您的缓存解决方案应考虑到这一点。本质上,我可能会建议使用 memcache。在大多数情况下,它会比访问数据存储更快,并且当您生成大块 HTML 时,缓存的主要好处之一是您可能不必承受访问数据的 I/O 损失店铺。如果您使用数据存储进行缓存,您仍然会受到 I/O 损失。除非您有一个非常复杂的页面,否则重新生成所有内容和从数据存储中的缓存 html 提取之间的差异可能相当小。最好从 memcache 中获得一堆非常快速的缓存命中并每隔一段时间进行一次完全重新生成,而不是每次都调用数据存储。当您更新时,没有什么可以阻止您在 memcache 中使缓存的 HTML 无效,并且如果您的流量足够高以保证它,您总是可以做一个多级缓存系统。

但是,我主要担心这是过早的优化。如果您还没有流量,请将缓存保持在最低限度。 App Engine 提供了一组非常方便的性能分析工具,您应该在获得至少几个 QPS 的流量后使用这些工具来识别瓶颈。

无论何时进行性能优化,都要先进行衡量!许多性能“优化”要么比原来的速度慢、完全相同,要么具有负面的用户体验特征(如陈旧的数据)。在确定必须优化之前不要进行优化。

【讨论】:

  • 嗨,鲍勃,感谢您的反馈!会考虑您的建议!
  • 如果您想在其中放入大量数据,绝对可以对 memcached 进行基准测试。在应用程序引擎上,它使用纯 python pickle 实现 iiirc 来腌制所有内容,这可能最终会很慢。
  • 如果他插入的对象是原始的预渲染 HTML,那没关系。
【解决方案2】:

不久前,我写了a series of blog posts 关于在 App Engine 上编写博客系统的文章。您可以在 static generation of HTML pages 上找到特别感兴趣的帖子。

【讨论】:

    【解决方案3】:

    这不是一个完整的解决方案,但可能会提供一些有趣的缓存选项。

    Google Appengine Frontend Caching 允许您在不使用 memcache 的情况下进行缓存。

    【讨论】:

      【解决方案4】:

      只需提供您网站的静态版本

      实际上比你想象的要容易得多。

      如果您已经有一个包含您网站的所有 url 的文件(例如 urls.py),那么已经完成了一半的工作。

      结构如下:

      +-/website
      +--/static
      +---/html
      +--/app/urls.py
      +--/app/routes.py
      +-/deploy.py
      

      /html 是提供静态文件的地方。 urls.py 包含您网站的所有 url 列表。 routes.py(如果您将路由移出 main.py)将需要修改,以便您可以在本地查看动态生成的版本,但在生产中提供静态版本。 deploy.py 是您的一站式静态站点生成器。

      你如何布局你的 urls 模块取决于。我个人使用它作为一站式商店来获取页面的所有元数据,但 YMMV 除外。

      例子:

      main = [
        { 'uri':'about-us', 'url':'/', 'template':'about-us.html', 'title':'About Us' }
      ]
      

      网站的所有网址都采用结构化格式,让您轻松抓取自己的网站。

      路由配置稍微复杂一些。我不会详细介绍,因为有太多不同的方法可以实现。重要的部分是检测您是在开发服务器还是生产服务器上运行所需的代码。

      这里是:

      # Detect whether this the 'Development' server
      DEV = os.environ['SERVER_SOFTWARE'].startswith('Dev')
      

      我更喜欢将它放在 main.py 中并在全球范围内公开它,因为我用它来打开/关闭其他东西,比如日志记录,但还是 YMMV。

      最后,你需要爬虫/编译器:

      import os
      import sys
      import urllib2
      from app.urls import main
      
      port = '8080'
      local_folder = os.getcwd() + os.sep + 'static' + os.sep + 'html' + os.sep
      print 'Outputting to: ' + local_folder
      
      print '\nCompiling:'
      for page in main:
        http = urllib2.urlopen('http://localhost:' + port + page['url'])
        file_name = page['template']
        path = local_folder + file_name
        local_file = open(path, 'w')
        local_file.write(http.read())
        local_file.close()
        print ' - ' + file_name + ' compiled successfully...'
      

      这确实是基本的东西。当我创建它时,我真的惊呆了。这实际上相当于在浏览器中逐页打开您的网站,另存为 html,然后将该文件复制到 /static/html 文件夹中。

      最好的部分是,/html 文件夹的工作方式与任何其他静态文件夹一样,因此它会自动被缓存,并且缓存过期时间将与所有其他静态文件相同。

      注意:这将处理所有页面都从根文件夹级别提供的站点。如果您需要更深层次的文件夹嵌套,则需要稍作修改来处理。

      【讨论】:

      • 嗨,埃文。虽然有用,但您的回答对 Alex 的环境做出了可能不正确的假设。如果他正在构建一个内容管理系统,管理员可以在其中独立于应用程序编辑内容,那么使用静态文件显然无法解决这个问题。考虑避免实际上不能回答问题的答案。一些社区成员会否决这些。如果您知道如何缓存生成的 HTML,我鼓励您详细说明。不过,我认为这是有用的信息,尤其是通过浏览器源将生成的 HTML 转换为静态 HTML 的步骤。
      • @jmort253 实际上,完全可以编辑内容并在开发服务器上动态显示。理想情况下,将内容推送到生产服务器涉及一键式编译/部署周期。动态路由(即编辑/管理工具)和动态站点文件在开发服务器上可用,而生产服务器指向站点的静态(即静态/html)版本。在生产方面,所有内容都保持静态,因此所有内容都会自动缓存。
      • (续)这可能不是标准/传统方法,但它有效。我目前将它用于开发服务器为 GAE 且生产服务器为 Apache 的 2 个站点(即使用正则表达式路由来设置正确的文件结构)。对于一体式 GAE 生产/开发设置也可以这样做,并且如果将编译器设置为作为 GAE 部署挂钩触发,则版本号将保持同步。见developers.google.com/appengine/articles/hooks。最小化 CPU 周期的最佳方法是永远不要在生产环境中启动动态代码...
      • (续)顺便说一句,所有静态文件都会自动缓存。您只需要在 app.yaml 中设置 defult_expiration 参数即可。见stackoverflow.com/questions/2642432/…
      • 嗨,埃文。我不是百分百清楚。不是因为你的解释,而是因为我对 Python 缺乏经验。您是否从开发服务器运行生产站点?或者您是否在 Apache 上提供静态文件,然后具有向 GAE 服务器发出 AJAX 请求的 JS 代码?另外,感谢您发送挂钩的链接。我需要读几遍才能真正沉浸其中并了解这一切如何适用于此。我目前正在自己​​寻找 GAE 上的动态缓存解决方案,但在 Java SDK 上;但是,这些概念当然也适用于此。
      【解决方案5】:

      旧线程,但随着技术的进步,我还是会发表评论...... 另一个可能适合也可能不适合您的想法是生成 HTML 并将其存储在 Google Cloud Storage 上。 然后通过云存储为您提供的 CDN 链接访问 HTML。 无需检查 memcache 或等待数据存储在新请求时唤醒。 我已经开始为我的 appengine 应用程序存储我所有的 JavaScript、CSS 和其他静态内容(图像、下载等),并且对我来说效果很好。

      【讨论】:

      • 此外,同一区域内的云服务之间的入口/出口是免费的:cloud.google.com/storage/pricing。因此,只要您不允许用户直接访问它,您只需为存储付费。如果您不想在静态资产上浪费任何内存缓存带宽,这可能会更可取。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-23
      • 1970-01-01
      • 2013-02-05
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多