【问题标题】:Correct way of setting up varnish for caching django sites为缓存 django 站点设置清漆的正确方法
【发布时间】:2013-11-28 14:30:36
【问题描述】:

我刚刚在我的后端服务器前面设置了一个只安装了清漆的服务器,在那里我有两个不同的 django 站点,通过 nginx+gunicorn 提供服务

它似乎有效,但我得到 Header Age = 0,并且查看文档,这不是很好。

我想为匿名用户缓存页面,但不为经过身份验证的用户或如果用户有一个名为“AUTHENTICATION”的 cookie 缓存

这是我的默认.vcl

backend django {
    .host = "backend1";
    .port = "8080";
}


sub vcl_recv {  

  # unless sessionid/csrftoken is in the request, don't pass ANY cookies (referral_source, utm, etc)  
  if (req.request == "GET" && (req.url ~ "^/static" || (req.http.cookie !~ "sessionid" && req.http.cookie !~ "csrftoken" && req.http.cookie !~ "AUTHENTICATION"))) {  
    remove req.http.Cookie;  
  }  


    #normalize accept-encoding to account for different browsers  
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            remove req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            remove req.http.Accept-Encoding;
        }
    }  



}  

sub vcl_fetch {  

  # /static and /media files always cached  
  if (req.url ~ "^/static" || req.url ~ "^/media") {  
       unset beresp.http.set-cookie;  
       return (deliver);  
  }  

  # pass through for anything with a session/csrftoken set  
  if (beresp.http.set-cookie ~ "sessionid" || beresp.http.set-cookie ~ "csrftoken" || beresp.http.set-cookie ~ "AUTHENTICATION") {  
    return (hit_for_pass);  
  } else {  
    return (deliver);  
  }  

} 

是否会为每个用户设置sessionid,即使他们没有登录,这会阻止 Varnish 有效地为匿名用户缓存页面?

编辑:

使用isvarnishworking.com这是输出:

HTTP/1.1 200 OK
Server: cloudflare-nginx
Date:   Fri, 15 Nov 2013 09:30:20 GMT
Content-Type:   text/html; charset=utf-8
Connection: keep-alive
Set-Cookie: __cfduid=d281023a84b2e5351d109c1848eeca1601384507820317; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.mydomain.com; HttpOnly
Vary:   Cookie
X-Frame-Options:    SAMEORIGIN
X-Varnish:  1602772074
Age:    0
Via:    1.1 varnish
CF-RAY: cdaec14fab00412
Content-Encoding:   gzip

编辑 2:

我的新 default.vcl:

backend django {
    .host = "backend1";
    .port = "8080";
}

sub vcl_recv {  

    #normalize accept-encoding to account for different browsers  
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            remove req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            remove req.http.Accept-Encoding;
        }
    }  
}



sub vcl_fetch {  
  if (req.url ~ "^/static" || req.url ~ "^/media") {  
    unset beresp.http.set-cookie;  
  }  

  if (beresp.http.set-cookie !~ "sessionid" && beresp.http.set-cookie !~ "csrftoken" && beresp.http.set-cookie !~ "AUTHENTICATION") {  
    unset beresp.http.set-cookie; 
  }
} 

来自isvarnishworking.com的结果

HTTP/1.1 200 OK
Server: cloudflare-nginx
Date:   Fri, 15 Nov 2013 12:08:42 GMT
Content-Type:   text/html; charset=utf-8
Connection: keep-alive
Set-Cookie: __cfduid=d55ea1b56e978cbbf3384d0fa2f21571e1384517322491; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.mydomain.com; HttpOnly
Vary:   Cookie
X-Frame-Options:    SAMEORIGIN
X-Varnish:  1240916568
Age:    0
Via:    1.1 varnish
CF-RAY: cdbd4119f3b0412
Content-Encoding:   gzip

编辑 3:

backend default {
    .host = "backend1";
    .port = "8080";
}

sub vcl_recv {  

  # unless sessionid/csrftoken is in the request, don't pass ANY cookies (referral_source, utm, etc)  
  if (req.request == "GET" && (req.url ~ "^/static" || (req.http.cookie !~ "sessionid" && req.http.cookie !~ "csrftoken" && req.http.cookie !~ "AUTHENTICATION"))) {  
    remove req.http.Cookie;  
  }  

    #normalize accept-encoding to account for different browsers  
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            remove req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            remove req.http.Accept-Encoding;
        }
    }  

}  

sub vcl_fetch {  

  # /static and /media files always cached  
  if (req.url ~ "^/static" || req.url ~ "^/media") {  
       unset beresp.http.set-cookie; 
  }

  if (beresp.http.set-cookie !~ "sessionid" && beresp.http.set-cookie !~ "csrftoken" && beresp.http.set-cookie !~ "AUTHENTICATION") {  
    unset beresp.http.set-cookie;
  }

} 

我的后端响应(前面没有清漆)是:

GET / HTTP/1.1
Host: www.mydomain.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: nb-no,nb;q=0.9,no-no;q=0.8,no;q=0.6,nn-no;q=0.5,nn;q=0.4,en-us;q=0.3,en;q=0.1
Accept-Encoding: gzip, deflate
Cookie: __cfduid=d8f496aef561efd7a30c3d9f909a02cf31384507505064; sessionid=twoq45r21gn341545ohubilyp739r42ee; _ga=GA1.2.382479980.1384507508
Connection: keep-alive

HTTP/1.1 200 OK
Server: cloudflare-nginx
Date: Fri, 15 Nov 2013 14:37:53 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Language, Cookie
X-Frame-Options: SAMEORIGIN
Content-Language: nb
CF-RAY: cdcae94f68105af
Content-Encoding: gzip

【问题讨论】:

    标签: django varnish varnish-vcl django-cache


    【解决方案1】:

    是否会为每个用户设置 sessionid,即使他们不是 登录,这会阻止 Varnish 有效地缓存页面 匿名用户?

    你是对的。注销后立即启动一个新会话,并在用户的机器上植入一个新的会话 cookie。为了解决这个问题,我创建了一个自定义注销视图,用于与 Varnish 一起使用的网站:

    from django.conf import settings
    from django.contrib.auth.views import logout
    
    def logout_user(request):
        """After logging out some of the cookies should be deleted,
        allowing upstream cache to work effectively."""
        response = logout(request)
        request.session.modified = False  # forces session middleware not to set its own cookie
        response.delete_cookie(settings.CSRF_COOKIE_NAME)
        response.delete_cookie(settings.SESSION_COOKIE_NAME)
        return response
    

    如您所见,我强制会话中间件不设置新的 cookie,然后删除旧的 cookie(我也删除了 csrf cookie)。

    编辑:另外,这段代码似乎完全没有必要,as Varnish do this automatically for any cookie being set

      # pass through for anything with a session/csrftoken set  
      if (beresp.http.set-cookie ~ "sessionid" || beresp.http.set-cookie ~ "csrftoken" || beresp.http.set-cookie ~ "AUTHENTICATION") {  
        return (hit_for_pass);  
      } else {  
        return (deliver);  
      }  
    

    另请注意,hit_for_pass 将使特定 URL 在几分钟内无法缓存(对于所有用户!)。试试这三种诊断方法:

    1. 清除cookies,删除上面的代码,重启Varnish,检查Age是否仍然设置为0
    2. 检查来自后端 (nginx) 的标头。可能是它自己设置了Age 值,或者强制 Varnish 使用其他缓存控制 cookie 这样做?
    3. 使用varlog 检查这些响应是否被缓存。

    编辑 2: 您的 isvarnishworking.com 输出显示服务器设置了一个名为 __cfduid 的 cookie。每次设置 cookie 时,Varnish 都会自动进入 hit-for-pass 模式(请参阅我在上面的编辑中链接到的代码)。这很可能是问题的原因。我想这就是我认为不必要的代码的原因。我会尝试明确删除所有未知的 cookie:

    sub vcl_fetch {  
      if (req.url ~ "^/static" || req.url ~ "^/media") {  
        unset beresp.http.set-cookie;  
      }  
    
      if (beresp.http.set-cookie !~ "sessionid" && beresp.http.set-cookie !~ "csrftoken" && beresp.http.set-cookie !~ "AUTHENTICATION") {  
        unset beresp.http.set-cookie; 
      }
    } 
    

    【讨论】:

    • 这似乎很重要,但是第一次访问该站点的用户/访问者呢?我要为其缓存网站的是那些人/用户。
    • 新用户不应设置sessionid,除非您以某种方式使用他们的会话。尝试做一个实验 - 清除你的 cookie 并检查你的网站是否在没有登录的情况下设置了 sessionid。
    • 你是对的!新用户没有设置sessionid,为什么varnish设置age为0?我对我的问题进行了编辑,以包含 isvarnishworking.com 的输出
    • 首先,/etc/sysconfig/varnish 中的 VARNISH_TTL 值是多少?
    • 使用默认值120
    【解决方案2】:

    开箱即用的清漆是为不发送 Vary: cookie 标头的损坏的应用程序服务器设置的。 Django 很聪明,如果你对会话 id 做任何事情,它会发送一个 Vary: cookie 头。所以最好的办法是编辑你的 vcl 并删除任何与 cookie 有关的内容。 Varnish 将自行处理变化。

    这里是默认的

    sub vcl_recv {
        if (req.restarts == 0) {
            if (req.http.x-forwarded-for) {
                set req.http.X-Forwarded-For =
                    req.http.X-Forwarded-For + ", " + client.ip;
            } else {
                set req.http.X-Forwarded-For = client.ip;
            }
        }
        if (req.request != "GET" &&
          req.request != "HEAD" &&
          req.request != "PUT" &&
          req.request != "POST" &&
          req.request != "TRACE" &&
          req.request != "OPTIONS" &&
          req.request != "DELETE") {
            /* Non-RFC2616 or CONNECT which is weird. */
            return (pipe);
        }
        if (req.request != "GET" && req.request != "HEAD") {
            /* We only deal with GET and HEAD by default */
            return (pass);
        }
        /// REMOVE THIS SECTION
        if (req.http.Authorization || req.http.Cookie) {
            /* Not cacheable by default */
            return (pass);
        }
        /// ENDREMOVE SECTION
        return (lookup);
    }
    

    此外,在您的 django 代码中,包括 python 和模板,请确保不要检查/使用 request.user 或 session。如果这样做,这意味着页面对不同用户的行为不同,因此应该以不同方式缓存(这就是 django 将发送 Vary: cookie 标头的原因,这意味着根据 cookie 以不同方式缓存它,即 sessionid)

    【讨论】:

    • 我仍在努力解决这一切。我只是使用 cloudflare,而不是清漆,但我认为原理是一样的。我正在查看登录和注销的请求和响应。我看到的唯一区别是会话 ID cookie。这告诉我 Cloudflare 无法区分登录和注销。据我所知,它会单独缓存每个人,因为它会在每个人的会话 ID cookie 上键入每个人。我在这里错过了什么?
    猜你喜欢
    • 2014-05-26
    • 2013-03-21
    • 2019-01-14
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 2014-10-02
    • 1970-01-01
    相关资源
    最近更新 更多