【问题标题】:Varnish: Clear Cache after POST data清漆:POST数据后清除缓存
【发布时间】:2013-01-09 04:56:16
【问题描述】:

我有一个位于 Varnish 后面的 Plone 网站。一切正常,除了一件事。

这是一个动态网站,因此会不时出现新内容。场景如下:

我有一个显示项目列表的页面。此页面已被缓存。所以我通过某种形式添加了另一个项目,并返回到同一页面,但新项目未显示。这是因为显示的页面来自缓存并且仍在其 TTL 内。

如何确保在提交新项目后,该页面会从缓存中清除,并且将显示来自后端服务器的包含新项目的新页面?

我的简单VCL如图:

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

sub vcl_recv { 
    if (req.request != "GET" && req.request != "HEAD") {
        # We only deal with GET and HEAD by default
        return (pass);
    }

    # remove unnecessary cookies 
    if (req.http.cookie ~ "wc.cookiecredentials|Path|Domain") { 
        # found wc.cookiecredentials in request, passing to backend server 
        return (lookup); 
    } else { 
        unset req.http.cookie; 
    } 
} 

sub vcl_fetch { 
    #unset beresp.http.Set-Cookie; 
    set beresp.ttl = 12h; 
    return(deliver); 
} 

# Routine used to determine the cache key if storing/retrieving a cached page. 
sub vcl_hash { 
    # Do NOT use this unless you want to store per-user caches. 
    if (req.http.Cookie) { 
        set req.hash += req.http.Cookie; 
    } 
} 

sub vcl_deliver { 
    # send some handy statistics back, useful for checking cache 
    if (obj.hits > 0) { 
        set resp.http.X-Cache-Action = "HIT"; 
        set resp.http.X-Cache-Hits = obj.hits; 
    } else { 
        set resp.http.X-Cache-Action = "MISS"; 
    } 
} 

或者说白了,当我收到 POST 请求时,如何清除或清除域的整个缓存?

【问题讨论】:

    标签: caching varnish varnish-vcl


    【解决方案1】:

    为此,您需要自定义 Varnish VCL 以处理 PURGE 请求和 Plone CMS,以便在内容更改时向 Varnish 发出清除请求。

    Plone 开发者文档在using Varnish with Plone 上有非常好的和详尽的文档。您可以根据自己的特定需求对其进行调整。

    文档中的示例解释了如何在 VCL 中创建自定义 ACL 和正则表达式清除处理,然后如何在 Plone 中使用它来清除整个缓存。我已经从此处的示例中复制了 VCL 和 Plone 视图,以防它们在未来某个时间从 Plone 站点中删除:

    acl purge {
            "localhost";
            # XXX: Add your local computer public IP here if you
            # want to test the code against the production server
            # from the development instance
    }
    
    ...
    
    sub vcl_recv {
    
            ...
    
            # Allow PURGE requests clearing everything
            if (req.request == "PURGE") {
                    if (!client.ip ~ purge) {
                            error 405 "Not allowed.";
                    }
                    # Purge for the current host using reg-ex from X-Purge-Regex header
                    purge("req.http.host == " req.http.host " && req.url ~ " req.http.X-Purge-Regex);
                    error 200 "Purged.";
            }
    }
    

    然后为 Plone 创建一个自定义视图,用于向 Varnish 发出 PURGE 请求:

    import requests
    
    from Products.CMFCore.interfaces import ISiteRoot
    from five import grok
    
    from requests.models import Request
    
    class Purge(grok.CodeView):
        """
        Purge upstream cache from all entries.
    
        This is ideal to hook up for admins e.g. through portal_actions menu.
    
        You can access it as admin::
    
            http://site.com/@@purge
    
        """
    
        grok.context(ISiteRoot)
    
        # Onlyl site admins can use this
        grok.require("cmf.ManagePortal")
    
        def render(self):
            """
            Call the parent cache using Requets Python library and issue PURGE command for all URLs.
    
            Pipe through the response as is.
            """
    
            # This is the root URL which will be purged
            # - you might want to have different value here if
            # your site has different URLs for manage and themed versions
            site_url = self.context.portal_url() + "/"
    
            headers = {
                       # Match all pages
                       "X-Purge-Regex" : ".*"
            }
    
            resp = requests.request("PURGE", site_url + "*", headers=headers)
    
            self.request.response["Content-type"] = "text/plain"
            text = []
    
            text.append("HTTP " + str(resp.status_code))
    
            # Dump response headers as is to the Plone user,
            # so he/she can diagnose the problem
            for key, value in resp.headers.items():
                text.append(str(key) + ": " + str(value))
    
            # Add payload message from the server (if any)
    
            if hasattr(resp, "body"):
                    text.append(str(resp.body))
    

    如前所述,这只是按需清除整个缓存。我不是 Plone 方面的专家,所以我无法详细回答如何调整它以清除特定内容。基本上,您需要确定在特定情况下需要清除哪些页面,然后调整上面的示例,以便在 Plone 中处理 POST 请求时自动向 Varnish 发出 PURGE 请求。

    仅在 VCL 中处理清除(即检测 POST 调用并基于这些调用清除内容)非常复杂。我相信在 Plone 中处理逻辑和清除会更有效。

    更新:

    如果您希望清除每个 POST 上的整个缓存,可以按以下方式完成。

    sub vcl_recv {
        if ( req.request == "POST") {
            ban("req.http.host == " + req.http.Host);
            return(pass);
        }
    }
    

    现在每个POST 请求都会导致缓存在同一主机名下的所有页面从缓存中清除。不过,我建议从长远来看研究早期的解决方案。当出现POST 时,仅清除实际需要清除的页面会更有效。

    【讨论】:

    • 感谢您的回答@Ketola。有没有办法在每次收到 POST 请求时清除整个缓存?
    • 确实有。我已经更新了我的答案以涵盖这个替代方案。不过,这不是一个非常有效的解决方案,在这种情况下,您的缓存命中率永远不会很高。
    • 这应该符合我现在的目的。
    猜你喜欢
    • 1970-01-01
    • 2012-02-19
    • 2017-07-21
    • 2022-11-28
    • 2012-08-07
    • 2017-10-20
    • 2012-04-20
    • 2012-11-12
    • 1970-01-01
    相关资源
    最近更新 更多