案例:varnish对某个动态页面(a.jsp)进行了缓存,且指定缓存时长为5分钟。如果在这5分钟内a.jsp的数据发生了变化,访问时仍会显示旧数据。直到缓存到期后varnish重新缓存才会显示新数据。这就导致页面不能及时显示更新信息。而且由于缓存时长只是一个估计值,如果设定的太长,则不能及时显示新数据,如果设定的太短,则可能浪费资源。

varnish缓存也可以通过后台管理手动执行命令进行清除,但这显然也不够智能。如果我们能够通过程序自动清除缓存就好了!比如我们在后台添加商品的时候对varnish缓存的商品列表进行清除,再次加载商品列表页面时varnish找不到缓存就会重新请求并缓存新的页面。此时的商品列表就是最新的了。

下面用一个简单的java程序实现对varnish缓存的清除。


首先,在varnish配置文件(默认为default.vcl)中对a.jsp页面进行缓存配置。

sub vcl_recv {
……其他配置信息
if (req.url ~ "^.*/a.jsp$") {
     return (lookup);
 }……其他配置信息
}

sub vcl_fetch {
……其他配置信息
if(req.url ~ "^.*/a.jsp$") {
           set beresp.ttl = 5m;
           return (deliver);
     }……其他配置信息
}


其次,在varnish配置文件中进行刷新缓存的处理。

#允许刷新缓存的规则
 acl purgeallow {
     "127.0.0.1";
     "172.16.15.110";//这个IP地址也可以发起刷新请求
 }sub vcl_recv {
if (req.request == "PURGE") {
          if (!client.ip ~ purgeallow) {
              error 405 "Not allowed";
          }
#转到hit或者miss处理
return (lookup);
      }}
sub vcl_hit {
      #刷新缓存的请求操作,设置TTL为0,返回处理结果代码
      if (req.request == "PURGE") {
          set obj.ttl = 0s;
error 200 "Purged";
      }
std.log("url hit! the url="+req.url);
#缓存服务器命中后(查找到了) 
      return (deliver);
  }
  
  sub vcl_miss {
      #刷新缓存的请求操作
      if (req.request == "PURGE") {
error 404 "Not in cache";
      }
      std.log("url miss! the url="+req.url);
#缓存服务器没有命中(去后台服务器取)
      return (fetch);
  }


最后,就是写个java程序进行测试了。

import java.io.IOException;
 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpMethod;
 import org.apache.commons.httpclient.HttpMethodBase;

 public class PurgeVarnish {
public void purge(String url){
HttpClient client = new HttpClient();
HttpMethod method = new PurgeMethod(url);
//添加头信息告诉服务端可以对Response进行GZip压缩。
//我在varnish配置文件的vcl_hash函数中对Accept-Encoding进行了hash,
//如果请求里面不带Accept-Encoding信息头的话,就找不到varnish中的缓存,导致无法清除缓存。
//vcl_hash函数中对Accept-Encoding进行了hash是因为有些浏览器可能不支持压缩,
//如果他们找到的缓存是被压缩过的,就会导致浏览器无法正确解析返回内容。
method.setRequestHeader("Accept-Encoding", "gzip, deflate");
try {
int status = client.executeMethod(method);
System.out.println("status==="+status);
} catch (IOException e) {
e.printStackTrace();
}finally{
method.releaseConnection();
}
}

public static void main(String[] args) {
PurgeVarnish purgeVarnish = new PurgeVarnish();
purgeVarnish.purge("http://172.16.15.111:1111/a.jsp");
}
 }


 class PurgeMethod extends HttpMethodBase {
public PurgeMethod() {
super();
setFollowRedirects(true);
}
public PurgeMethod(String url) {
super(url);
setFollowRedirects(true);
}


@Override
public String getName() {
return "PURGE";
}

 }


注意我在method.setRequestHeader("Accept-Encoding", "gzip, deflate");这一行上面添加的注释。如果你不想添加这个信息头,只要确保varnish配置文件的val_hash函数中没有对Accept-Encoding进行hash即可。


好了,下面怎么测试就不用说了吧。