20

在一个流量很大的新项目中,我们正在考虑如何构建我们的 Symfony2 应用程序以利用缓存,并准备好在未来变得更加积极。我很想知道你的意见。

假设用户向页面请求地点列表。这个页面有:

- list
   - common data (title, author, description)
   - user data (the user likes the list + other data)
- first 20 places
   - common data (title, photo of each place)
   - user data (the rates of the user for those places)

HTML 可能类似于:

<html>...
<body>
  <header>
  ...
  <!-- Embed the top user menu -->
  <esi:include src="http://example.com/profile/menu" />
  ...
  </header>

  <content>
  ...
  common data of the list
  ...
  <!-- Embed the common data of the first 20 places, the same for everyone -->
  <esi:include src="http://example.com/lists/17/places" />
  ...
  <!-- Embed the user data of the list (used in JS) -->
  <esi:include src="http://example.com/lists/17/user" />
  ...
  <!-- Embed the user data of the list of places (used in JS) -->
  <esi:include src="http://example.com/lists/17/places/user" />
  ...
  </content>
</body>       
</html>

HTML 将缓存在网关上(Symfony 或 Varnish)。地点列表也将大部分时间缓存在网关上。用户数据请求将是那些被调用但不被缓存的(至少最初不是)。

问题

  1. 你觉得这个结构怎么样?
  2. 如果用户是匿名的,我可以避免为用户数据制作 esi-includes 吗?另外,如果我有匿名用户的 cookie?如何?
  3. 用户菜单的 esi-include 是否有意义?
  4. 还是我们应该忘记 ESI 并始终通过控制器(例如缓存公共数据的渲染视图)?
  5. 我们是否应该将要求用户数据的 2 个 ESI 请求移动为 AJAX 调用,而不是在服务器上等待?
  6. 如果我们需要快速扩展,这是一种很好的扩展方法吗?什么是最好的?

多谢!

4

1 回答 1

5

我们在一个站点上使用 Varnish 进行整页缓存,并且我已经使用 Symfony2 几年了,但请记住,我没有在任何生产环境中使用 Varnish + Symfony2 + ESI。

  1. 我认为基本的想法是可以的。如果许多页面中的菜单相同,并且许多页面上的位置列表也相同,则您将获得由 Varnish 或 Symfony 反向缓存缓存的常见内容。由于 Varnish 通常将缓存保存在内存中,因此您可以更快地获取内容,并且不必在每次请求时调用渲染和数据库查询代码。

    如果用户登录,困难的部分是缓存这些 ESI 请求。据我所知,在默认的 Varnish 配置中,其中包含 Cookie 的请求永远不会被缓存。如果您倾向于将 cookie 传递给 ESI 请求,则这些 ESI 响应将不会在用户之间共享。

    您可以尝试从 URL 制定一些规则,但如果您使用默认的 Symfony 树枝助手,生成的 URL 是 /_internal/...,因此可能很难区分公共和私有的。

    如果通过,您也可以配置为始终忽略任何 cookie Cache-Control: public。这在 Symfony 中默认完成:

    if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) {
        $response->setPrivate(true);
    }
    

    正如您从代码中看到的,如果您有public指令,响应将永远不会是私有的。

    我还没有发现 Varnish 是如何处理这个指令的——据我所知,它不会缓存任何默认具有 cookie 的请求。所以我认为你必须调整配置才能做到这一点。

  2. 如果主页也将被缓存,我看不出您如何跳过包含。

    我假设您的注册用户(不是搜索机器人)需要 JS,所以我建议使用 Javascript 来区分用户数据的加载。

    Javascript 代码可以查看用户是否有 cookiesession-id等,并仅在这种情况下请求获取数据。设置一些其他 cookie 也可能是个好主意,例如_loggedin避免 Javascript 代码获取会话 ID。

    未登录的用户也可以在 cookie 中保存一些数据,例如_likedPost:1,2,132. Javascript 可以获取此 cookie 并进行一些 HTML 更正,甚至无需发出额外的请求。

    正如我们对这些 cookie 所做的那样:我们将 JS-only cookie 与应用程序 cookie 分开。我们通过某种模式来做到这一点,比如_\wJS cookie。然后我们调整了 Varnish 配置以拆分 Cookie 标头并删除这些仅限 JS 的 cookie。然后,如果没有其他 cookie,则与所有人共享响应。应用程序(Symfony)没有得到这些 cookie,因为它们被剥离了。

  3. 我认为如果每一页都相同的话。

  4. 我认为 ESI 很好,因为 Varnish 可以将缓存保存在内存中。因此,它甚至可能不会对您的硬盘进行任何内容查询。由于您的控制器缓存可能也在内存中,我认为 Varnish 会比 Symfony 框架更快地查找缓存,其中包含所有路由、PHP 代码、服务初始化等。

  5. 这取决于,但我认为这可能是更好的方法。请记住,缓存过着不同的生活。例如,如果您的地点列表缓存了 2 小时,则在此时间结束时地点可能已更改 - 一些新项目是列表中的新项目,而其中一些已丢失。您给用户的列表仍然是旧列表(缓存),但您提供了有关新列表的用户数据 - 有些数据不需要,有些数据丢失。

    It might be better approach to get loaded places by javascript, for example searching for some HTML attribute like data-list-item-id and then make ajax request querying data about these items. In this case your user data will be synchronized with current cached list and you can make 1 ajax request for both lists instead of 2.

  6. If cache invalidation (PURGE requests) are not used, all HTTP cache sheme is indeed good to scale. You can scale the application to several servers and configure Varnish to call them randomly, by some rule or just to use one of them as a failsafe. If the bandwidth is still too big, you can always modify the cache timeouts and other configuration.

于 2012-12-11T22:39:08.597 回答