【问题标题】:How to Deal with Cookies in Varnish stack如何处理 Varnish 堆栈中的 Cookie
【发布时间】:2013-04-19 00:27:05
【问题描述】:

由于网站性能缓慢,我开始寻找 info Varnish 作为缓存解决方案,并对 Google Analytics 有一些疑问。

当站点上有 5K 活跃用户时(根据 GA 的实时流量报告),后端服务器上的服务器负载飙升至 30-40 +,乘客队列开始堆积,站点几乎无法使用。我知道为了获得更好的性能需要缓慢的查询和数据库工作,但目前我没有资源来优化查询和数据库架构、索引等,所以考虑添加清漆。

我创建了一个图表以更好地显示堆栈,这是当前堆栈的外观:(该站点当前在 CDN - Akamai 中缓存图像/css/js)

我想在后端服务器前端添加两个 varnish 实例来缓存文章,堆栈将如下所示:

该站点是一个新闻站点,我正在寻找有关如何正确处理 cookie 和缓存的建议。对于第 1 阶段,我想完全排除经过身份验证的用户并提供动态内容,因为同时经过身份验证的用户并不多。

混淆在于 Google Analytic 的 cookie。我的理解是,谷歌使用 javascript 在客户端上设置了一个 cookie,客户端直接与谷歌通信,所以后端不需要客户端发送的 GA cookie,在 vcl_recv 子例程期间取消设置它们是安全的。

sub vcl_recv {

// Remove has_js and Google Analytics __* cookies.
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");
// Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
}

问题

  • 这是一种安全的方法吗?
  • Google 是否仍能正确跟踪,包括重复访问者?
  • 在我的第 1 阶段政策中,我还有什么需要注意的吗?

由于默认情况下 varnish 不会缓存任何设置了 cookie 的内容,因此仅通过添加删除 GA cookie 的策略来实现上述堆栈是否安全?我知道如果不微调 VCL 策略,我不会获得高命中率,但是在我的测试中,似乎即使在后端服务器前面使用默认清漆,也有 30% 的命中率,在分析这些之后,我看到大多数是 js/css 和图像文件,因此很明显,其中一些静态文件不是由 Akamai 甚至 Apache 提供的,而是传递给Passenger/Rails 以提供静态文件。这绝对需要纠正。

  • Varnish 会在默认情况下提高性能吗?

我是 varnish 的新手,因此非常感谢任何关于 varnish 或我提出的堆栈的额外细节/建议。

对于第 2 阶段 +

由于内容得到更新,我计划在两个清漆服务器上执行清除,由后端服务器在应用更改时触发,例如用户评论、页面浏览量等。

有很多未更新的存档文章,永久缓存它们是否安全?

由于我打算使用 RAM 进行清漆存储,我是否应该有额外的(第三个)清漆并使用磁盘进行存储,以明确那些存档页面。也许在清漆服务器前面添加 nginx 堆栈以将流量引导到存档内容的特定清漆实例? 负载均衡器->一对Nginx反向代理>一对清漆->(清漆LB到8个后端服务器)

我也很欣赏有关架构的任何建议。如果您需要更多详细信息以提供更好的建议,请告诉我,我很乐意为您提供更多详细信息。

【问题讨论】:

    标签: linux cookies nginx passenger varnish


    【解决方案1】:

    这是很多问题! :-)

    Q. Is this a safe approach?
    

    从表面上看,我会这么说。

    通常,在流量大且内容快速变化的新闻网站上设置 Varnish 可能是一项挑战。

    一个非常好的检查方法是构建一个清漆盒并让它直接访问您的集群(而不是通过负载平衡器),并为其提供一个临时公共 IP 地址。这将使您有机会针对 VCL 更改进行测试。您将能够测试评论、登录(如果有的话)以及其他任何事情,以确保没有意外。

    Q. Will Google still track properly, including repeat visitors?
    

    是的。 cookie 仅在客户端使用。

    您应该注意的一件事是,当后端发送 cookie 时,Varnish 也不会缓存内容。您将需要删除 vcl_fetch 上不需要的任何 cookie。如果使用 cookie 跟踪用户状态,这可能是个问题。

    Q. Is there anything else that I need to watch for in my policies for phase1?
    

    您需要在 Rails 中禁用 rack-cache,并设置您自己的标头。请注意,如果您删除 varnish,Rails 将在没有缓存的情况下运行,并且可能会崩溃!

    这就是我在 production.rb 中的内容:

      # We do not use Rack::Cache but rely on Varnish instead
      config.middleware.delete Rack::Cache
      # varnish does not support etags or conditional gets
      # to the backend (which is this app) so remove them too
      config.middleware.delete Rack::ETag
      config.middleware.delete Rack::ConditionalGet
    

    在我的 application_controller 我有这个私有方法:

    def set_public_cache_control(duration)
      if current_user
        response.headers["Cache-Control"] = "max-age=0, private, must-revalidate"
      else
        expires_in duration, :public => true
        response.headers["Expires"] = CGI.rfc1123_date(Time.now + duration)
      end
    end
    

    这是在我的其他控制器中调用的,因此我可以非常精细地控制对网站各个部分应用多少 chacheing。我在作为 before_filter 运行的每个控制器中使用设置方法:

    def setup
      set_public_cache_control 10.minutes
    end
    

    (application_controller 有过滤器和空白设置方法,所以在其他控制器中可以是可选的)

    如果您有部分网站不需要 cookie,您可以根据 VCL 中的 URL 将其剥离,并应用标头。

    您可以像这样在 apache 配置中为静态资产设置缓存时间(假设您使用的是默认资产路径):

    <LocationMatch "^/assets/.*$">
        Header unset ETag
        FileETag None
        # RFC says only cache for 1 year
        ExpiresActive On
        ExpiresDefault "access plus 1 year"
        Header append Cache-Control "public"
    </LocationMatch>
    
    <LocationMatch "^/favicon\.(ico|png)$">
        Header unset ETag
        FileETag None
        ExpiresActive On
        ExpiresDefault "access plus 1 day"
        Header append Cache-Control "public"
    </LocationMatch>
    
    <LocationMatch "^/robots.txt$">
        Header unset ETag
        FileETag None
        ExpiresActive On
        ExpiresDefault "access plus 1 hour"
        Header append Cache-Control "public"
    </LocationMatch>
    

    这些标头将被发送到您的 CDN,这将使资产缓存更长时间。观察 varnish,您仍然会看到请求以下降的速度进入。

    我还会在页面不需要 cookie 但经常更改的所有内容上设置非常短的缓存。在我的例子中,我为主页设置了 10 秒的缓存时间。这对 Varnish 意味着,每 10 秒就会有一个用户请求发送到后端。

    您还应该考虑将 varnish 设置为使用宽限模式。这允许它从缓存中提供稍微陈旧的内容,而不是将访问者暴露在后端对刚刚过期的项目的缓慢响应中。

    Q. There are plenty of archived articles that don't get updated, is it safe to cache them forever?
    

    为此,您需要更改您的应用,以便为那些已归档的文章发送不同的标题。这假设他们不会有 cookie。根据我在我的网站上所做的,我会这样做:-

    在上面的设置中添加一个条件来改变缓存时间:

    def setup
      # check if it is old. This code could be anything
      if news.last_updated_at < 1.months.ago
        set_public_cache_control 1.year
      else
        set_public_cache_control 10.minutes
      end
    end 
    

    这会设置一个公共标头,因此 Varnish 会缓存它(如果没有 cookie),任何远程缓存(在 ISP 或公司网关处)也会如此。

    问题在于,如果您想删除故事或更新故事(例如,出于法律原因)。

    在这种情况下,您应该向 Varnish 发送一个私有标头来更改该 URL 的 TTL,但为其他所有人发送一个较短的公共标头。

    这将允许您将 Varnish 设置为(例如)1 年的内容提供服务,同时它会发送标头以告诉客户每 10 分钟回来一次。

    在这些情况下,您需要添加清除清漆的机制。

    为了让您开始,我的 application_controller 中有第二种方法:

    def set_private_cache_control(duration=5.seconds)
      # logged in users never have cached content so no TTL allowed
      if ! current_user
        # This header MUST be a string or the app will crash
        if duration
          response.headers["X-Varnish-TTL"] = duration.to_s
        end
      end
    end
    

    在我的 vcl_fetch 我有这个:

    call set_varnish_ttl_from_header;
    

    vcl 函数是这样的:

    sub set_varnish_ttl_from_header {
      if (beresp.http.X-Varnish-TTL) {
        C{  
          char *x_end = 0;
          const char *x_hdr_val = VRT_GetHdr(sp, HDR_BERESP, "\016X-Varnish-TTL:"); /* "\016" is length of header plus colon in octal */
          if (x_hdr_val) {
            long x_cache_ttl = strtol(x_hdr_val, &x_end, 0);
            if (ERANGE != errno && x_end != x_hdr_val && x_cache_ttl >= 0 && x_cache_ttl < INT_MAX) {
              VRT_l_beresp_ttl(sp, (x_cache_ttl * 1));
            }
          }
        }C
        remove beresp.http.X-Varnish-TTL;
      }
    }
    

    这样头部就不会被传递(s-max-age 会)到任何上游缓存。

    设置方法如下所示:

    def setup
      # check if it is old. This code could be anything
      if news.last_updated_at < 1.months.ago
        set_public_cache_control 10.minutes
        set_private_cache_control 1.year
      else
        set_public_cache_control 10.minutes
      end
    end 
    

    如有任何补充问题,请随时提出,我会更新此答案!

    【讨论】:

    • Richard,首先非常感谢您抽出这么多时间提供如此详细的解释,并附赠配置后端 ruby​​ 应用程序的材料。我将与我们的开发团队合作,并用测试结果更新帖子。我肯定会问补充问题,因为你提供了:)
    • 不用担心。让应用程序和 Varnish 协同工作至关重要。我自己对 Varnish 还是很陌生,但是有很多资源可以解决你将面临的一些真正困难的问题。顺便说一句,我们没有对我们的网站发表评论,因此我们可以在一天的高峰时段获得高达 85% 的缓存命中率。
    猜你喜欢
    • 2012-08-06
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 2021-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多