侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

缓存(之一) 使用Apache Httpd实现http缓存

2022-07-03 星期日 / 0 评论 / 0 点赞 / 65 阅读 / 16243 字

HTTP性能的问题与方案 一个最终用户访问一个网页,从浏览器发出请求,到接受请求,时间大体上消耗在了以下几个部分: 建立tcp/ip握手连接。 浏览器向服务器传送请求数据。 服务器处理数据。

HTTP性能的问题与方案

一个最终用户访问一个网页,从浏览器发出请求,到接受请求,时间大体上消耗在了以下几个部分:

  1. 建立tcp/ip握手连接。

  2. 浏览器向服务器传送请求数据。

  3. 服务器处理数据。

  4. 服务器返回数据。

如果用户请求的资源很少改变,像js,css,图片之类的静态文件,如果每次用户的请求都需要占用服务器资源去处理,再如果一个用户和服务器位于太平洋两岸,那么,时间就被浪费在了网络传输和服务器处理步骤上了。在这种情况下,应该使用cache。

像上图一样,在离用户最近的地方,增加一个缓存服务器,将不常修改的静态文件缓存起来,用户的请求就可以直接由缓存服务器来处理,而不需要再劳烦网站服务器了。

一般缓存服务器,代理服务器和逆向代理服务器在一起,即,一个Apache Httpd上,同时开启了缓存功能与代理功能。只是这台服务器从不同的功能角度,被称为不同的名字,有时叫缓存服务器,有时叫代理服务器。

Http缓存机制

http缓存是遵循http协议实现的。控制缓存行为的字段均在http header中。这些字段分别在几个方面控制着缓存:

  • 有效期机制

  • 重验证机制

  • 共享机制

有效期机制

控制缓存有效期的有下面几个header,我必须将它们列出来。

计算一个缓存是否有效,只需要计算缓存的使用期(cache age)和新鲜生存期(freshness lifetime)。缓存有效的判别式为:

isValid = cache_age < freshness_lifetime;

新鲜生存期比较好计算,应该为Cache-Control:max-age的值。如果没有Cache-Control:max-age的话,则使用Expires,如果都没有,则使用试探时算法(略)。计算式如下:

if (cache-control_max_age_set) fressness_lifetime = Cache-Control:max-age;else if (Expires_set) freshness_lifetime = Expires - Date;else 试探算法。

为何Expires比max-age给替代了?因为Expires有可能是各级代理服务器写入的,但Date是由原始服务器写入的,各个服务器的时钟可能存在差异,其准确度无法得到保证。

缓存使用期则是比较难计算的。它不能简单的用当前时间去减原始服务器写入的Date,原因是各个服务器的时钟差异,有的可能差几天。所以,它的计算依靠每一级代理服务器返回缓存时所带的age头部。对于离服务器最近的一级代理服务器,age头部的算法应该为:

age = (response_arrive_time - request_sent_time) + (now - response_arrived_time) - 修正值; //此计算并不精确,但误差在秒级。

那下一级代理的age算法应该为:

age = age_of_cache_returen_from_last_proxy + cache_time_saved_in_this_proxy;

递归下去,可以计算出每一级代理的age头部。

重验证机制

下面列出了几个重要的头部,还有一些没有列在其中。

以上的几个头部,组成了cache的重验证机制。前3个头部是对cache强制认证的约束,后面4个则是实体认证时所需要检测的属性。

需要注意no-cache和must-revalidate的区别。no-cache表明缓存在被返回之前,必须向服务器冲验证是否被修改过。有时候,代理允许使用过期的cache,比如,服务器死机了,代理的cache虽然已经过期,但还是可以被使用。must-revalidate并不是要求cache一定要重验证,未过期的cache不会因它的存在而去服务器重新验证。must-revalidate是为了禁止这种行为。它要求过期的缓存一定要重验证通过后才能使用,如果服务器死机了,那无法进行重验证,过期的cache也就不能再被使用,用户收到504 gateway timout。 must-revalidate对有效期内的cache并无作用。

共享机制

缓存应该存在什么地方,则由下面的三个头部组成。

Cache的流程图

因为整个过程中存在很多的条件节点,时序图并不好表示出因果关系,所以还是用流程图更好一些。我想了很久,才想出如何用一种更直观的方法来画出cache工作的流程图。

黄色线代表request的流程,蓝色线代表response的流程,红色线代表原始服务器处理流程。

Apache Httpd搭建Cache Server

软件需要

  1. Tomcat 8

  2. Apache Httpd 2.2

服务器开发

我制作一个图片展示页面。页面上展示4张图片,每张图片的响应都包含不同的cache header。这四张图片如下:

为此,我可以专门写一个filter,来管理这些header。Last-Modified和ETag头会由tomcat的default servlet自动添加。我就不自己去处理了。此filter在doFilter方法上加了synchronized修饰,为了让请求按顺序执行,这样我们就可以看清楚每个请求和返回的header都有什么。我把header都打印出来了。

@WebFilter(urlPatterns={"*.jpg"}) public class CacheFilter implements Filter {    private int count = 0;    public void init(FilterConfig filterConfig) throws ServletException {}    public void destroy() {}        public synchronized void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        String uri = req.getRequestURI();        System.out.println("["+count+++"]Accessing "+uri);        printRequestHeaders(req);                HttpServletResponse resp = (HttpServletResponse) response;        if (uri.endsWith("public.jpg")) {            resp.addHeader("Cache-Control", "max-age=600,public");        }        if (uri.endsWith("private.jpg")) {            resp.addHeader("Cache-Control", "max-age=600,private");        }        if (uri.endsWith("noStore.jpg")) {            resp.addHeader("Cache-Control", "no-store");        }        if (uri.endsWith("noCache.jpg")) {            resp.addHeader("Cache-Control", "no-cache");        }        chain.doFilter(request, response);        printResponseHeaders(resp);    }        private void printRequestHeaders(HttpServletRequest request) {        System.out.println("Request headers:");        Enumeration<String> names = request.getHeaderNames();        while (names.hasMoreElements()) {            String name = names.nextElement();            Enumeration<String> values = request.getHeaders(name);            while (values.hasMoreElements()) {                System.out.println(name + " : " + values.nextElement());                        }        }        System.out.println();    }        private void printResponseHeaders(HttpServletResponse response) {        System.out.println("Response headers:");        Collection<String> names = response.getHeaderNames();        for (String name : names) {            Collection<String> values = response.getHeaders(name);            for (String value : values) {                System.out.println(name + " : " + value);            }        }        System.out.println("**********************************************");    }}

创建一个web app, 名叫cache。将4张图片复制到webapp/img/目录下。创建上面的filter。

最后在index.html上面展示:

<html><body><div><img src="/cache/img/public.jpg"/></div><div><img src="/cache/img/private.jpg"/></div><div><img src="/cache/img/noStore.jpg"/></div><div><img src="/cache/img/noCache.jpg"/></div></body></html>

打包,并部署到tomcat服务器。

缓存服务器开发

自从Apache 2.2开始,cache_module已经成为非常稳定模块。它完全遵循HTTP协议中的cache规定。所以我们可以使用cache_module来做实现Apache Httpd的缓存管理。

cache_module是由两个实现模块mem_cache_module和disk_cache_module所支持的。其中mem_cache_module是缓存于内存中,disk_cache_module则存于硬盘上。各种详细的命令可以参照Apache手册。本实验,只使用cache_module和disk_cache_module来管理HTTP缓存。只涉及到了部分命令。

修改Apache下的httpd.cnf

LoadModule cache_module modules/mod_cache.soLoadModule disk_cache_module modules/mod_disk_cache.soLoadModule proxy_module modules/mod_proxy.soLoadModule proxy_ajp_module modules/mod_proxy_ajp.so

然后修改Apache下的httpd-vhosts.conf。

NameVirtualHost *:80<VirtualHost *:80>   ServerAdmin joey   ServerName www.test.com   ErrorLog "logs/cache.log"   CustomLog "logs/accessCache.log" common      # 开启debug   LogLevel debug      ProxyRequests Off   ProxyPreserveHost On      ProxyPass / ajp://localhost:9009/   ProxyPassReverse / ajp://localhost:9009/      # 如果header中没有max-age或者expires,则添加默认过期时间,默认1小时.   CacheDefaultExpire 3600   # cache存放的目录   CacheRoot D:/share/tmp   # 对pattern为/cache的url开启磁盘缓存。   CacheEnable disk /cache   CacheDirLevels 5   CacheDirLength 3   UseCanonicalName On</VirtualHost>

在apache2.2中,cache的log还不完善。如果使用2.4版本,可以开启关于cache命中决策的log。2.2中,只能通过在ErrorLog中搜索mod_cache.c关键字,查看cache的处理日志。

检测结果

使用检测工具为Fiddler2, 一个可以捕获所有浏览器对外交流的软件。还可以使用Firebug, chrome debug tool。

在执行下面步骤之前,必须确保明白一件重要的事情,下面所有的步骤都是在浏览器地址栏输入地址后,按enter键产生的。不可以使用刷新键。

  1. (ie)用ie首次访问http://www.test.com/cache 

    在代理服务器端查看cache.log,或cache文件夹。在浏览器端,可以使用Fiddler和web developer tool等工具。可以发现对4张图片请求的处理:


  2. (ie,10分钟内)在浏览器缓存和代理缓存均未过期,chrome访问http://www.test.com/cache

  3. (chrome,10分钟内)在代理缓存新鲜期内,换一个浏览器,chrome访问http://www.test.com/cache

  4. (chrome,10分钟后)在10分钟后,代理的缓存和浏览器的缓存都过期了。chrome访问http://www.test.com/cache

  5. (ie,在第4步后,立刻)这时候,ie的缓存已经过期,但proxy端的缓存是新的。

定期清理没用的cache磁盘

使用apache的bin目录下的htcacheclean命令,定期清理cache磁盘。htcacheclean可以手工运行,也可以做成守护线程,周期性运行。运行手册http://httpd.apache.org/docs/2.2/programs/htcacheclean.html 

广告 广告

评论区