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

   2023-02-10 学习力0
核心提示:http://www.tuicool.com/articles/EFfeu2HTTP性能的问题与方案一个最终用户访问一个网页,从浏览器发出请求,到接受请求,时间大体上消耗在了以下几个部分:建立tcp/ip握手连接。浏览器向服务器传送请求数据。服务器处理数据。服务器返回数据。如果用户请求的

http://www.tuicool.com/articles/EFfeu2

HTTP性能的问题与方案

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

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

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

  3. 服务器处理数据。

  4. 服务器返回数据。

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

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

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

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

Http缓存机制

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

  • 有效期机制

  • 重验证机制

  • 共享机制 

有效期机制

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

头名 作用域 描述
Response 原始服务器产生响应的时间,GMT格式。必须由服务器产生,代理不允许修改此值。
Response

每一级缓存发出cache时,跟Date头部相比,cache已经被使用的时间,以秒为单位。

1.1中Age必须出现在cache中。应有各级代理产生。

Expires Response 缓存过期时间,GMT格式。可由代理修改。
Cache-Control: max-age=xx Request和Response

在response中声明此缓存保持新鲜的生命时间。

在request中发送max-age=0,效果则同no-cache。

Cache-Control: no-cache:这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。 

Cache-Control: no-store:这个才是响应不被缓存的意思。 

Pragma: no-cache:跟Cache-Control: no-cache相同,Pragma: no-cache兼容http 1.0 ,Cache-Control: no-cache是http 1.1提供的。因此,Pragma: no-cache可以应用到http 1.0 和http 1.1,而Cache-Control: no-cache只能应用于http 1.1.

计算一个缓存是否有效,只需要计算缓存的使用期(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-Control: no-catch Request和response 请求必须对缓存进行重验证。有些服务器略有出路,把no-cache与no-store混淆。
Pragma: no-catch Request和response 请求必须对缓存进行重验证。为了向http1.0兼容。
Cache-Control: must-revalidate response 强制约束此cache在过期后,必须通过重新验证以后才可使用。
Last-Modified Response 返回的内容上次被修改的日期的long值,精确到秒。
If-Modified-Since Request 请求时带上过期缓存的Last-Modified值。
Response 响应实体的标签。
If-None-Match Request 请求时带上过期缓存的标签。

以上的几个头部,组成了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-Control: public Response 共享缓存。缓存可被代理服务器存储。
Cache-Control: private Response 私有缓存,缓存不能被代理存储,可由浏览器存储。
Cache-Control: no-store Request和Response 不可存储缓存。

Cache的流程图

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

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

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

Apache Httpd搭建Cache Server 

软件需要

  1. Tomcat 8

  2. Apache Httpd 2.2

服务器开发

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

图片名 public.jpg private.jpg noStore.jpg noCache.jpg
http header

Cache-Control: public, max-age=600

Last-Modified:xxxxxx

Etag:xxxxx

Cache-Control: private, max-age=600

Last-Modified:xxxxxx

Etag:xxxxx

Cache-Control: no-store

Cache-Control: no-cache

Last-Modified:xxxxxx

Etag:xxxxx

为此,我可以专门写一个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.so
LoadModule disk_cache_module modules/mod_disk_cache.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule 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
  CacheRoot D:/share/tmp
  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文件夹,可以发现代理服务器对4张图片的处理:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Action request to tomcat 
      status 200
      request to tomcat 
      status 200
      request to tomcat 
      status 200
      request to tomcat 
      status 200
      Handle cache cached 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached 
      reason: private
      not cached 
      reason: no-store
      cached 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT 

      浏览器端对4张图片的处理:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Response status 200 200 200 200
      Handle cache cached 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      cached 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached
      reason: no-store
      cached 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
    2. (ie,10分钟内)在浏览器缓存和代理缓存均未过期,chrome访问http://www.test.com/cache

      代理服务器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Action n/a request to tomcat 
      status 200
      revalidate to tomcat 
      status 304
      Handle cache n/a n/a not cached 
      reason: no-store
      Replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT

      浏览器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Response status use cache directly use cache directly 200 304
      Handle cache n/a n/a not cached 
      reason: no-store
      Replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
    3. (chrome,10分钟内)在代理缓存新鲜期内,换一个浏览器,chrome访问http://www.test.com/cache

      代理服务器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Action cache is fresh. 
      use cache.
      request to tomcat 
      status 200
      request to tomcat 
      status 200
      revalidate to tomcat 
      status 304
      Handle cache not cached 
      reason: private
      not cached 
      reason: no-store
      Replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT 

      浏览器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Response status 200 200 200 200
      Handle cache cached 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      cached 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached
      reason: no-store
      cached 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
    4. (chrome,10分钟后)在10分钟后,代理的缓存和浏览器的缓存都过期了。chrome访问 http://www.test.com/cache

      代理服务器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Action revalidate to tomcat
      status 304
      revalidate to tomcat 
      status 304
      request to tomcat 
      status 200
      revalidate to tomcat
      status 304
      Handle cache Replace cache header 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached 
      reason: private
      not cached 
      reason: no-store
      Replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT

      浏览器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Response status 304 304 200 304
      Handle cache replace cache header 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      replace cache header 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached
      reason: no-store
      replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
    5. (ie,在第4步后,立刻)这时候,ie的缓存已经过期,但proxy端的缓存是新的。

      代理服务器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Action cache is fresh, 
      use cache
      revalidate to tomcat 
      status 304
      request to tomcat 
      status 200
      revalidate to tomcat 
      status 304
      Handle cache not cached 
      reason: private
      not cached 
      reason: no-store
      Replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT

      浏览器端:

      Name public.jpg private.jpg noStore.jpg noCache.jpg
      Response status 304 304 200 304
      Handle cache replace cache header 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      replace cache header 
      max-age=600 
      Etag: XXXXX 
      Last-Modified: XXXX GMT
      not cached
      reason: no-store
      replace cache header 
      max-age=0 
      Etag: XXXXX 
      Last-Modified: XXXX GMT

      apache 缓存配置

      http://kinglord2010.iteye.com/blog/1442868
    6. 硬盘缓存:mod_disk_cache,依赖 mod_cache 模块

      内存缓存:mod_mem_cache,依赖 mod_cache 模块

      文件缓存:mod_file_cache 搭配 mod_mem_cache 模块使用 

      1、硬盘缓存:

      配置例子: 

       

      Js代码  缓存(之一) 使用Apache Httpd实现http缓存
      1. <IfModule mod_disk_cache.c>   
      2. CacheDefaultExpire 86400   
      3. CacheEnable disk /   
      4. CacheRoot /tmp/apacheCache   
      5. CacheDirLevels 5   
      6. CacheDirLength 5   
      7. CacheMaxFileSize 1048576   
      8. CacheMinFileSize 10   
      9. </IfModule>   

       


      CacheDefaultExpire: 设定缓存过期的时间(秒),默认是1小时,只有当缓存的文档没有设置过期时间或最后修改时间时这个指令才会生效 
      CacheEnable:启用缓存,第1个参数是缓存类型,第2个参数是缓存路径,指的是 url 路径,这里是缓存所有的东西,直接写上“/”即可,如“/docs”则只缓存 /docs 下的所有文件 
      CacheRoot:缓存文件所在的目录,运行 Apache 的用户(如daemon 或 nobody)要能对其进行读写,如果不清楚的话可以直接设置成 777,请手动建立该目录并设置好访问权限 
      CacheDirLevels:缓存目录的深度,默认是3,这里设置为5 
      CacheDirLength:缓存目录名的字符长度,默认是4,这里设置为5 
      CacheMaxFileSize 和 CacheMinFileSize :缓存文件的最大值和最小值(byte),当超过这个范围时将不再缓存,这里设置为 1M 和 10bytes 

      2、内存缓存: 

       

      Js代码  缓存(之一) 使用Apache Httpd实现http缓存
      1. <IfModule mod_mem_cache.c>   
      2. CacheEnable mem /   
      3. MCacheMaxObjectCount 20000   
      4. MCacheMaxObjectSize 1048576   
      5. MCacheMaxStreamingBuffer 65536   
      6. MCacheMinObjectSize 10   
      7. MCacheRemovalAlgorithm GDSF   
      8. MCacheSize 131072   
      9. </IfModule>   

       

       
      CacheEnable:启用缓存,使用基于内存的方式存储 
      MCacheMaxObjectCount:在内存中最多能存储缓存对象的个数,默认是1009,这里设置为20000 
      MCacheMaxObjectSize:单个缓存对象最大为 1M,默认是10000bytes 
      MCacheMaxStreamingBuffer:在缓冲区最多能够放置多少的将要被缓存对象的尺寸,这里设置为 65536,该值通常小于100000或 MCacheMaxObjectSize 设置的值 
      MCacheMinObjectSize:单个缓存对象最小为10bytes,默认为1bytes 
      MCacheRemovalAlgorithm:清除缓存所使用的算法,默认是 GDSF,还有一个是LRU,可以查一下 Apache 的官方文档,上面有些介绍 
      MCacheSize:缓存数据最多能使用的内存,单位是 kb,默认是100kb,这里设置为128M

      3、 文件缓存:

       #缓存内容
      MMapFile /var/www/html/index.html /var/www/html/articles/index.html 
      #只缓存文件的句柄 
      CacheFile /var/www/html/index.html /var/www/html/articles/index.html 

      信息参考:http://httpd.apache.org/docs/2.2/caching.html 

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Apache80端口被占用解决办法
    Apache80端口被占用解决办法
    1. win+R,输入 cmd,打开命令行窗口2. 命令行输入netstat -ano3. 找到80端口及对应进程 4. 在任务管理器中的进程处查看与上述80端口对应的PID相同的进程,并关闭。如果没有PID,选择“查看”--"选择列"--勾选“PID”。4. 关闭进程。如果进程为上述系统服务,
    03-08
  • Hadoop中mapreduce运行WordCount程序报错Error:
    这个问题是因为map的方法参数与继承mapper定义的参数类型不一致导致的,应该将Mapper的key参数类型设置成Object,就可以解决这个问题 
    03-08
  • 使用 Apache Hudi 实现 SCD-2(渐变维度)
    使用 Apache Hudi 实现 SCD-2(渐变维度)
    数据是当今分析世界的宝贵资产。 在向最终用户提供数据时,跟踪数据在一段时间内的变化非常重要。 渐变维度 (SCD) 是随时间推移存储和管理当前和历史数据的维度。 在 SCD 的类型中,我们将特别关注类型 2(SCD 2),它保留了值的完整历史。 每条记录都包含有
    03-08
  • 一个用 Python 分析 Apache 日志的故事
    一个用 Python 分析 Apache 日志的故事
    介绍不久前,公司里有人告诉我“我想知道 Apache 的访问日志是否可以用来做一些事情”。数据分析,Apache,发文章,我是初学者,写的不好请见谅。访问日志分析导入模块我正在使用以下模块。# データの処理import pandas as pdimport numpy as np# グラフ表示i
    03-08
  • [转]用apache反向代理解决单外网ip对应内网多个
    用apache反向代理解决单外网ip对应内网多个web主机的问题  转载一个有独立外网IP,需内网服务器对外发布的例子,是应用apache虚拟主机的。来源地址:http://www.itshantou.com/Servers/web/06/10/44219.html    几年前开始在学校的服务器上建网站,那时
    02-10
  • Apache service named reported the following
    apache启动失败报错:The Apache service named reported the following error: AH00451: no listening sockets available, shutting down . The Apache service named reported the following error: (OS 10055)由于系统缓冲区空间不足或队列已满,不能执行
    02-10
  • struts布局管理---SiteMesh一个优于Apache Tile
    1. SiteMesh的基本原理       一个请求到服务器后,如果该请求需要sitemesh装饰,服务器先解释被请求的资源,然后根据配置文件 获得用于该请求的装饰器,最后用装饰器装饰被请求资源,将结果一同返回给客户端浏览器。 2. 如何使用SiteMesh    这里以st
    02-10
  • linux 安装 apache2.2.31
     Linux下安装和配置Apache 概要:本文介绍在CentOS5.4 Linux中安装和配置Apache2.2.14,并且实现Apache和Tomcat6的整合。文章分为三部分,分别是删除系统自带的Apache、安装Apache2.2.14和配置Apache2.2.14。 文章中介绍的知识也可以在其它版本的Linux中
    02-10
  • Apache CXF使用Jetty发布WebService
    Apache CXF使用Jetty发布WebService
    一、概述Apache CXF提供了用于方便地构建和开发WebService的可靠基础架构。它允许创建高性能和可扩展的服务,可以部署在Tomcat和基于Spring的轻量级容器中,也可以部署在更高级的服务器上,例如Jboss、WebSphere或WebLogic。 CXF提供了以下功能:WebService
    02-10
  • apache下ab.exe使用方法。。 apache ab工具
    自己在cmd中写了半天的路径也没有写对。。最后网上的一个哥们告诉我说没有共同语言了。。。毛线啊 差距确实很大!大能猫死panda早晚干掉你,叫你丫整天嘲讽我!比如我的ab.exe在D盘的wamp文件夹下apache文件夹下bin文件夹下。那么在cmd中可以这么写:"D:\wamp
    02-10
点击排行