如何使用ESI构建Symfony2应用程序?

fes*_*sja 20 caching varnish symfony esi

在一个拥有大量流量的新项目中,我们正在思考如何构建我们的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)
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

问题:

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

非常感谢!

Mar*_*tis 5

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

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

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

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

    您也可以配置为Cache-Control: public在传递时始终忽略任何cookie .默认情况下,这在Symfony中完成:

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

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

    我还没有找到Varnish如何处理这个指令 - 据我所知,它没有默认缓存任何有cookie的请求.所以我认为你必须调整配置才能实现这一目标.

  2. 如果还要缓存主页面,我看不到你如何跳过包含.

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

    如果用户有cookie session-id等,则可以查看Javascript代码,并仅在此情况下请求获取数据.设置其他cookie可能也是一个好主意,比如_loggedin避免Javascript代码获取会话ID.

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

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

  3. 我认为如果在每个页面中它都是相同的话.

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

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

    通过javascript获取加载位置可能是更好的方法,例如搜索某些HTML属性data-list-item-id,然后发出ajax请求查询有关这些项的数据.在这种情况下,您的用户数据将与当前缓存列表同步,您可以为两个列表而不是2列表发出1 ajax请求.

  6. 如果未使用高速缓存失效(PURGE请求),则所有HTTP高速缓存都可以很好地扩展.您可以将应用程序扩展到多个服务器,并按照某种规则配置Varnish随机调用它们,或者只使用其中一个作为故障保护.如果带宽仍然太大,您可以随时修改缓存超时和其他配置.