`
wbj0110
  • 浏览: 1603669 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

基于资源的HTTP Cache的实现介绍

阅读更多

我们都知道浏览器会缓存访问过网站的网页,浏览器通过URL地址访问一个网页,显示网页内容的同时会在电脑上面缓存网页内容。如果网页没有更新的话,浏览器再次访问这个URL地址的时候,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。

什么是HTTP Cache

对于浏览器的这种网页缓存机制大家已经耳熟能详了,举个例子来说,JavaEye的新闻订阅地址:http://www.iteye.com/rss/news , 当浏览器或者订阅程序访问这个URL地址的时候,JavaEye的服务器在response的header里面会发送给浏览器如下状态标识:

Etag    "427fe7b6442f2096dff4f92339305444"
Last-Modified   Fri, 04 Sep 2009 05:55:43 GMT

这就是告诉浏览器,新闻订阅这个网络资源的最后修改时间和Etag。于是浏览器把这两个状态信息连同网页内容在本地进行缓存,当浏览器再次访问JavaEye新闻订阅地址的时候,浏览器会发送如下两个状态标识给JavaEye服务器:

If-None-Match   "427fe7b6442f2096dff4f92339305444"
If-Modified-Since   Fri, 04 Sep 2009 05:55:43 GMT

就是告诉服务器,我本地缓存的网页最后修改时间和Etag是什么,请问你服务器的资源有没有在我上次访问之后有更新啊?于是JavaEye服务器会核对一下,如果该用户上次访问之后没有更新过新闻,那么根本就不必生成这个RSS了,直接告诉浏览器:“没什么新东西,你还是看自己缓存的网页吧”,于是服务器就发送一个304 Not Modified的消息,其他什么都不用干了。

这就是HTTP层的Cache,使用这种基于资源的缓存机制,不但大大节省服务器程序资源,而且还减少了网页下载次数,节约了很多网络带宽。

HTTP Cache究竟有什么作用?

我们通常的动态网站编程,服务器端程序根本就不去处理浏览器发送过来的If-None-Match和If-Modified-Since状态标识,只要有请求就生成网页发送给浏览器。对于一般情况来说,用户不会总是没完没了刷新一个页面,所以大家并不认为这种基于资源的缓存有什么太大的作用,但实际情况并非如此:

1、像Google这种比较智能的网络爬虫可以有效识别资源的状态信息,如果使用这种缓存机制,可以大大减少爬虫的爬取次数。

比方说Google每天爬JavaEye网站大概15万次左右,但实际上JavaEye每天有更新的内容不会超过1万个网页。因为很多内容更新比较快,因此Google就会反复不停的爬取,这样本身就造成了很多资源的浪费。如果我们使用HTTP Cache,那么只有当网页内容发生改变的时候,才会真正进行爬取,其他时候我们直接告诉Google的爬虫304 Not Modified就可以了。这样不但降低了服务器本身的负载和爬虫造成的网络带宽消耗,实际上也大大提高了Google爬虫的工作效率,岂不是皆大欢喜?

2、很多内容更新不频繁的网页,尽管用户不会频繁的刷新,但是从一个比较长的时间段来看使用HTTP Cache,仍然可以起到很大的缓存作用。

比方说一些历史讨论帖子,已经过去了几个月了,这些帖子内容很少更新。用户可能通过搜索,收藏链接,文章关联等方式时不时访问到这个页面。那么只要用户访问过一次以后,后续所有访问服务器直接发送304 Not Modified就可以了,不用真正生成页面。

3、对于历史帖子使用HTTP Cache可以避免爬虫反复的爬取。

比方说JavaEye的论坛帖子列表页面,分页到20页后面的帖子已经很少有人直接访问了,但是从服务器日志去看,每天仍然有大量爬虫反复爬取这些分页到很后面的页面。这些页面由于用户很少去点击,所以基本上没有被应用程序的memcached缓存住,每次访问都会造成很高的资源消耗,爬虫隔一段时间就爬一次,对服务器是很大的负担。如果使用了HTTP Cache,那么只要爬虫爬过一次以后,以后无论爬虫爬多少次,都可以直接返回304 Not Modified了,极大的节省了服务器的负载。

如何在应用程序里面使用HTTP Cache

如果我们要在自己的程序里面实现HTTP Cache,是件非常简单的事情,特别是对Rails来说只需要添加一点点代码,以上面的JavaEye新闻订阅来说,只要添加一行代码:

def news
  fresh_when(:last_modified => News.last.created_at, :etag => News.last)
end

用最新新闻文章作为Etag,该文章最后修改时间作为资源的最后修改时间,这样就OK了。如果浏览器发送过来的标识和服务器标识一致,说明内容没有更新,直接发送304 Not Modified;如果不一致,说明内容更新,浏览器本地的缓存太古老了,那么就需要服务器真正生成页面了。

以上只是一个最简单的例子,如果我们需要根据状态做一些更多的工作也是很容易的。比方说JavaEye博客的RSS订阅地址: http://robbin.iteye.com/rss

@blogs = @blog_owner.last_blogs
@hash = @blogs.collect{|b| {b.id => b.post.modified_at.to_i + b.posts_count}}.hash
if stale?(:last_modified => (@blog_owner.last_blog.post.modified_at || @blog_owner.last_blog.post.created_at), :etag => @hash)
  render :template => "rss/blog"
end

这个实现稍微复杂一些。我们需要判断博客订阅所有的输出文章是否有更新,所以我们用博客文章内容最后修改时间和博客的评论数量做一个hash,然后用这个hash值作为资源的Etag,那么只要这些博客文章当中任何文章内容被修改,或者有新评论,都会改变Etag值,从而通知浏览器内容有更新了。

除了RSS订阅之外,JavaEye网站还有很多地方适合使用HTTP Cache,比方说JavaEye论坛的版面列表页面,一些经常喜欢泡论坛的用户,可能时不时会上来刷新一下版面, 看看有没有新的帖子,那么我们就不必每次用户请求的时候都去执行程序,生成页面给他。我们判断一下如果没有新帖子的话,直接告诉他304 Not Modified就可以了,在没有使用HTTP Cache之前的版面Action代码:

def board
  @topics = @forum.topics.paginate...
  @announcements = (params[:page] || 1).to_i == 1 ? Topic.find :all, :conditions => ...
  render :action => 'show'
end

添加HTTP Cache以后,代码如下:

def board
  @topics = @forum.topics.paginate...
  if logged_in? || stale?(:last_modified => @topics[0].last_post.created_at, :etag => @topics.collect{|t| {t.id => t.posts_count}}.hash)
    @announcements = (params[:page] || 1).to_i == 1 ? Topic.find :all, :conditions...
    render :action => 'show'
  end
end

对于登录用户,不使用HTTP Cache,这是因为登录用户需要实时接收站内短信通知和订阅通知,因此我们只能对匿名用户使用HTTP Cache,然后我们使用当前所有帖子id和回帖数构造hash作Etag,这样只要当前分页列表页面有任何帖子发生改变或者有了新回帖,就更新页面,否则就不必重新生成页面。

论坛帖子页面实际上也可以使用HTTP Cache,只不过Etag的hash算法稍微复杂一些,需要保证帖子的任何改动都要引起hash值的改变,示例代码如下:

def show
  @topic = Topic.find params[:id]
  user_session.update_.......  if logged_in?
  Topic.increment_counter(...) if ......
  @posts = @topic.post_by_page params[:page]
  posts_hash = @posts.collect{|p| {p.id => p.modified_at}}.hash
  topic_hash = @topic.forum_id + @topic.sys_tag_id.to_i + @topic.title.hash + @topic.status_flag.hash
  ad_hash = ...  (广告的hash算法,略)
  if logged_in? || stale?(:etag => [posts_hash, topic_hash, ad_hash])
    render
  end  
end

分别根据主题贴,该分页的所有回帖和帖子页面的广告内容进行hash,计算出来一个唯一的Etag值,保证任何改动都会生成新的Etag,这样就搞定了,是不是很简单!这种帖子的缓存非常有效,可以避免Rails去render页面和下载页面,极大的减轻了服务器负载和带宽。

再举一个需求比较特殊的例子:对于知识库搜索相关文章的推荐页面,比方说:http://www.iteye.com/wiki/topic/462476 也就是本文的相关文章推荐内容,我们并不希望用户和爬虫每次访问这个页面都实际执行一遍全文检索,然后构造页面内容,在一个相对不长的时间范围内,这篇文章的相关推荐文章改变的概率不大,因此我们希望比方说5天之内,用户重复访问该页面,就直接返回304 Not Modified,那么Rails没有直接的设施给我们使用,需要我们稍微了解一些Rails的机制,自己编写,代码示例如下:

def topic
  @topic = Topic.find(params[:id])
  unless logged_in?
    if request.not_modified?(5.days.ago)
      head :not_modified
    else
      response.last_modified = Time.now
    end
  end
end    

每次用户请求,我们判断用户是否5天之内访问过该页面,如果访问过,直接返回304 Not Modified,如果没有访问过,或者上次访问已经超过了5天,那么设置最近修改时间为当前时间,然后生成页面给用户。是不是很简单?

 
分享到:
评论

相关推荐

    基于C语言的Cache模拟器实验.zip

    资源包含文件:设计报告word+PPT+源码及可执行exe文件 ...Linux 64-bit ,C 语言实现一个高效的模拟器,详解介绍参考:https://biyezuopin.blog.csdn.net/article/details/122684339?spm=1001.2014.3001.5502

    Laravel开发-laravel-httpcache

    `laravel-httpcache`是Laravel 5中用于实现这一功能的组件,它基于Symfony的HttpCache库。下面我们将深入探讨`laravel-httpcache`的原理、配置和使用方法。 首先,了解HTTP缓存的基本概念。HTTP缓存主要分为两种...

    springboot1.x基于spring注解实现J2Cache两级缓存集成

    在本文中,我们将深入探讨如何在Spring Boot 1.x版本中使用Spring注解来实现J2Cache的两级缓存机制,其中包括一级缓存Ehcache和二级缓存Redis。通过这种方式,我们可以显著提高应用程序的性能,减少对数据库的依赖,...

    基于Verilog带cache和中断的五级流水通用处理器的设计与实现.pdf

    "基于Verilog带cache和中断的五级流水通用处理器的设计与实现" 本文档介绍了基于Verilog设计的一种带cache和中断的五级流水通用处理器。该处理器采用五级流水CPU架构,即取值、译码、执行、访存和回写,实现了多...

    Cache模拟器

    程序使用C/C++混合编程,基本实现的Cache的模拟功能(通过读取trace文件得到相应的命中率),能够实现直接映射、全相联、组相联三种映射方式,其中全相联和组相联能够实现随机、LRU两种替换策略。目前三种映射方式均...

    facebook flashcache安装资源

    Facebook FlashCache是一款开源的缓存解决方案,主要用于提升类Unix系统...通过合理配置和管理,FlashCache可以帮助你优化硬件资源,实现更高的I/O效率。在实际操作中,务必遵循安全规范,以免造成数据丢失或系统损坏。

    基于共享总线的多处理器cache一致性的硬件实现.pdf

    本文主要关注的是基于共享总线的多处理器cache一致性硬件实现。 共享总线的多处理器系统中,每个处理器都有自己的私有cache,它们通过共享的总线进行通信。当一个处理器更新其cache中的数据时,这个变化需要被其他...

    基于java的的HTTP代理服务器 Smart Cache.zip

    【标题】"基于Java的HTTP代理服务器Smart Cache"是一个实现HTTP代理功能的软件,它使用Java编程语言开发,旨在提供高效的数据缓存服务。通过在客户端与目标服务器之间扮演中间角色,Smart Cache能够减少网络延迟,...

    linux 内核 cache 管理介绍

    LRU基于“最近最少使用的页面最先被淘汰”的原则,当内存不足时,最近最少使用的页面会被淘汰出缓存,为新数据腾出空间。此外,还有VFS(Virtual File System)层的缓存压力机制,根据整个系统的需求动态调整各类型...

    基于SoC协处理器Cache的动态分配方法.pdf

    【摘要】中提到的基于SoC协处理器Cache的动态分配方法,旨在优化片上存储资源,提升私有Cache的利用率,减少芯片面积。在多核SoC系统中,每个处理器通常配备有自己的私有Cache,然而,由于私有Cache的容量有限,可能...

    基于Java的的HTTP代理服务器 Smart Cache.zip

    【标题】"基于Java的HTTP代理服务器Smart Cache"是一个使用Java编程语言实现的代理服务器程序,它具有缓存功能,可以提高HTTP请求的响应速度和效率。通过在客户端和目标服务器之间扮演中介角色,Smart Cache能够减少...

    Java中各类Cache机制实现解决方案

    本文将详细介绍几种常见的Java缓存机制及其实现方法,帮助开发者更好地理解和应用这些技术。 #### 二、OSCache **OSCache** 是一个功能强大的开源Java EE缓存框架。它可以用于缓存JSP页面、HTTP响应以及Java对象等...

    一种基于路由器Cache的一致性协议.pdf

    首先,文章介绍了一种新的路由器Cache组织方式,这种结构旨在减少跨节点通信时的数据传输延迟。路由器Cache被设计为存储中间节点的数据副本,可以在数据请求经过路由器时提供快速访问。这减少了处理机之间必须等待...

    4路组cache,verilog实现

    本项目将介绍如何使用Verilog硬件描述语言来实现这样的4路组关联Cache。 首先,理解4路组关联的基本概念至关重要。在4路组关联的Cache中,主内存地址被映射到特定的缓存组,而每个组可以存储4条不同的数据。当...

    bcache,dm-cache,flashcache源码

    **bcache** bcache 是一个Linux内核模块,它将固态硬盘...总的来说,bcache、dm-cache和flashcache的源码提供了丰富的学习资源,对于从事系统优化、存储技术研究或内核开发的专业人士来说,它们无疑是宝贵的参考资料。

    Spring 与Ehcache实现基于方法的缓存

    本篇文章将详细探讨如何在Spring框架中集成并实现基于方法的缓存机制,利用Ehcache来优化数据访问。 首先,我们需要理解Spring的AOP概念,AOP允许我们定义横切关注点,如日志、事务管理或,正如在这个案例中,缓存...

    基于JSP+SqlServer实现的Caché的实验室资源管理系统(源代码+论文).zip

    【作品名称】:基于JSP+SqlServer实现的Caché的实验室资源管理系统(源代码+论文) 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。

    dns_parse-master_dns_dnscache缓存实现_middlecjc_directionlem_dnshash

    在这个特定的项目“dns_parse-master_dns_dnscache缓存实现_middlecjc_directionlem_dnshash”中,重点在于实现一个高效的DNS缓存系统,以提高DNS查询的响应速度和减少网络延迟。 DNS缓存是解决DNS查询效率问题的一...

Global site tag (gtag.js) - Google Analytics