`
robbin
  • 浏览: 4797715 次
  • 性别: Icon_minigender_1
  • 来自: 上海
博客专栏
377a9ecd-1ea1-34ac-9530-9daa53bb2a7b
robbin谈管理
浏览量:135666
社区版块
存档分类
最新评论

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

    博客分类:
  • Ruby
阅读更多
我们都知道浏览器会缓存访问过网站的网页,浏览器通过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天,那么设置最近修改时间为当前时间,然后生成页面给用户。是不是很简单?

在给JavaEye网站所有的RSS订阅输出添加了HTTP Cache以后,通过一天的观察发现,超过一半的RSS订阅请求已经被缓存了,直接返回304 Not Modified,所以效果非常明显,由于JavaEye网站每天RSS订阅的动态请求就超过了10万次,因此添加HTTP Cache可以减轻不少服务器的负担和带宽消耗。除此之外,新闻文章页面,整个论坛频道,知识库相关推荐文章页面都可以添加HTTP Cache,粗粗计算下来,JavaEye这些页面统统使用HTTP Cache以后,网站整体性能至少可以提高10%。

分享到:
评论
16 楼 liusong1111 2010-09-03  
这是robbin去年这时候发的,楼上给的那个链接明显是转载的.

一年过去了,随着IE9的表态,chrome extension/app store的布署,其它浏览器的跟进,html5的发展形势更加明朗,而html5里的application cache,local storage,web database等东西将给http cache增色一个等级,对于激进的技术人员来说,可以想像的空间相当大。

rails3的发布,在我看来,只是偿还以前欠下的架构债务,给了使用者一个完美的交待。rails的成长最让我感慨的是,它一步步印证了其核心团队敏锐且准确的技术嗅觉,从CoC、REST、rack,the next big thing是啥泥?从rails3主力yehuda以前写的一篇文章看,rails3对html5的全面支持将是一个重要方向。太好了。

搜rails3 yehuda mobile webinar应该能找到。

--------
恶,居然搜出的链接都不好使。我机器上有一份,分享一下。
15 楼 xsbird 2010-09-03  
http://dev.firnow.com/course/3_program/java/javajs/20100719/460432.html 被。。。,呵呵
14 楼 shenzhe 2009-11-12  
以前也发过一篇类似的,像客户端可以做很多服务器端的事,理论上来讲javascript可以完全代替服务器端。(除了直接与数据库交互)
13 楼 liuchong14 2009-10-21  
这个话题有够旧的。。。(我就温故知新吧)

貌似javaeye 没有设置Last-Modified 这个HTTP 头,而是设置了Etag 和Cache-Control(并且还是禁用了缓存耶)。

这少,至少这个页面是这样的。
12 楼 rainchen 2009-10-06  
rainchen 写道

....
最后修改时间的显示,也都可以用JS来完成。
....


刚说完就有了,还是同一天发表的,真是无巧不成书
http://vinsol.com/blog/2009/09/07/rails-caching-and-javascript-techniques/
11 楼 luolonghao 2009-10-06  
不错,加深了我对HTTP Cache的理解。
10 楼 xhanxhanxhan 2009-10-04  
挺受教。
感觉robbin对javaeye的优化该快把服务器性能榨干了把
9 楼 wuhua 2009-09-18  
HTTP Cache内存是很丰富了 robbin只是说到了比较常用的方式。
8 楼 flyerhzm 2009-09-16  
sorphi 写道
既然应用已经做了这些基础设施,那么在客户端和应用之间再加一层cache proxy就齐活了。


你需要的是Rack::Cache
7 楼 jones_ahk 2009-09-16  
没有用过ruby,如果是portal来用这个东西就不太现实了吧?!
那要每个组件的实例都需要判断后才能使用,无疑给服务器更大的压力!
6 楼 sorphi 2009-09-16  
既然应用已经做了这些基础设施,那么在客户端和应用之间再加一层cache proxy就齐活了。
5 楼 robbin 2009-09-15  
yanglaoshi5891 写道
问两个问题:
1.爬虫每次访问页面会对页面的点击率有影响吗?特别是对于一些采用通用计算点击率的地方。
2.如果使用了HTTP Cache后,点击率是不是会受到很大的影响,毕竟很多公司是很看重点击率的。


1、很多网站号称自己PV如何如何,几百万几千万PV,其实真实PV可能都不到一百万。就是因为他们是通过统计服务器日志来计算的。现在很多号称五百万到八百万PV的网站其实真实流量还不如JavaEye,但是JavaEye只号称自己一百万PV。要看真实PV,可以用Google Analytics,这个是相当准确的。爬虫流量不会导致GA统计数字上升,可以准确的排除爬虫流量。

2、用HTTP Cache以后,不会影响点击率,因为像GA这种流量统计系统是在页面里面嵌入js,虽然浏览器并不真正从服务器下载页面,但是还是需要真实渲染页面和执行js请求的,所以GA统计的流量不会有任何损失。

4 楼 yanglaoshi5891 2009-09-15  
问两个问题:
1.爬虫每次访问页面会对页面的点击率有影响吗?特别是对于一些采用通用计算点击率的地方。
2.如果使用了HTTP Cache后,点击率是不是会受到很大的影响,毕竟很多公司是很看重点击率的。
3 楼 dboylx 2009-09-08  
我们在设计的时候已经被浏览器的大冒子捂住了很多的创意。有很多东西都是可以拿来到客户端来做的,像负载,故障转移,缓存,balabala...
2 楼 wq163 2009-09-08  
不错,大家都在做类似memcached的缓存,却忽略了http协议本身的缓存,这种缓存能够让浏览器直接读取本地的内容,大大减少与服务端通信,效果非常好。最近在项目中就使用过一次
1 楼 rainchen 2009-09-07  
关于论坛的主题页面的缓存,大并发量时,还是建议使用静态页面缓存,左侧的用户信息,可以用一条AJAX触发,把当前页的相关用户ID一次过提交,请求结果处理中使用MEMCACHED缓存用户信息(文章,积分,等级等)。
最后修改时间的显示,也都可以用JS来完成。
那么资源的有效期查询都只需交给HTTP SERVER快速完成响应判断。
而且爬虫爬时,不会触发JS,也会省掉用户信息的查询请求。

RSS的输出同样也可以生成静态缓存,被动式过期更新(新文章发布时过期掉缓存),只是如果要在RSS输出中包含文章评论数等动态数据时,那么除了做被动的过期更新外,还得做个定时主动过期更新(适合较长时间内无新主贴,但回复跟帖频繁的情况),无法做到实时统计,毕竟RSS里的评论数只是辅助信息,不是很必要实时。

楼下的接着的分享经验。

相关推荐

    Cache模拟器

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

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

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

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

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

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

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

    论文研究-基于指令Cache作废的多核处理器同步技术.pdf

    提出了一种用于同步数据触发结构多核处理器的基于指令Cache作废的同步技术,同步时作废将执行的指令Cache行导致取指失效,向L2 Cache发送取指请求,L2 Cache中设置相应的过滤机制,不服务不满足同步条件的处理器核的...

    基于springboot+MyBatis实现的某房产平台系统源码+项目说明(毕设).zip

    1、基于springboot+MyBatis实现的某房产平台系统源码+项目说明(毕设).zip 2、该资源包括项目的全部源码,下载可以直接使用! 3、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,...

    JSP基于Caché的实验室资源管理系统的设计(源代码+lw).zip

    这个项目是一个基于Java语言开发的Web应用程序,采用SSM(Spring+SpringMVC+MyBatis)或SSH(Spring+SpringMVC+Hibernate)框架进行开发,使用MySQL作为数据存储,JSP作为页面开发。 项目的目标是构建一个高效、可靠...

    基于Python实现的HTTP代理服务器设计.zip

    (2)设计并实现一个支持Cache功能的HTTP代理服务器。要求能缓存原服务器响应的对象,并能够通过修改请求报文(添加if-modified-since头行),向原服务器确认缓存对象是否是最新版本。(选作内容,加分项目,可以当堂...

    机器学习基于python和rasa框架实现的订机票对话机器人demo源码+项目说明.zip

    机器学习基于python和rasa框架实现的订机票对话机器人demo源码+项目说明.zip 【资源介绍】 该项目是个人毕设项目,答辩评审分达到95分,代码都经过调试测试,确保可以运行!欢迎下载使用,可用于小白学习、进阶。 该...

    基于Django和Python的缓存锁拓展设计源码

    本资源提供了一套基于Django和Python的缓存锁拓展设计源码,包含20个文件,其中包括11个Python源代码文件...这些文件详细展示了如何使用Django的内置cache实现一个lock拓展,非常适合用于学习和参考Django项目的开发。

    HTML5使用ApplicationCache接口实现离线缓存技术解决离线难题

    简介 离线访问对基于网络的应用而言越来越重要。虽然所有浏览器都有缓存机制,但它们并不可靠,也不一定总能起到预期的作用。HTML5 使用 ApplicationCache 接口解决了由离线带来的部分难题。 使用缓存接口可为您的...

    Zan是基于PHP协程的网络服务框架.rar

    基于模型驱动的 SQLMap,实现了 SQL 的快速定位及方便的 sharding、cache 支持 提供类似于 Laravel 的 middleware(Filters & Terminators) 机制 Di及单元测试的良好支持 完整的RPC远程服务调用方案 框架定位 ...

    Java资源包01

    GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以...

    TCP-HTTP-操作系统.pdf

    HTTP 状态码 2xx:表示报文被成功接收 3xx:表示重定向,客户端...If-Modified-Since 和 Last-Modified:基于时间实现,在发现请求到的资源携带 Last-Modified,那 么在下一次请求时,会将其值携带在If-Modified-Since

    基于vue和springboot的客户管理系统源码+项目说明(毕设).zip

    4、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于vue和springboot的客户管理系统源码+项目说明(毕设).zip 前端:1.Node.js 2.Npm 3.Vue 4.Vue cli 5.Element 6....

    基于python的智能面试系统源码+项目说明+数据.zip

    3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于python的智能面试系统源码+项目说明+数据.zip ### 环境配置 1. 在环境变量中设置OPENAI_API_KEY 2. 安装依赖 `...

    基于LLM的智能智能面试系统python源代码+项目使用说明.zip

    <项目介绍> 项目概况 基于LLM的智能智能面试系统DEMO 环境配置 在环境变量中设置OPENAI_API_KEY 安装依赖 pip install -r requirements.txt NOTE: MAC M1版本使用一下命令安装依赖 pip install --no-cache-dir -...

    IntelX86系列CPU模拟器的研究与实现

    该多核模拟器在龙芯2号单处理器核的基础上,完整地模拟了基于目录的Cache一致性协议和存储转发式片上互联网络的结构模型,详细地刻画了由于系统乱序处理各种请求应答和请求之间的冲突而造成的时序特性,可以通过运行...

    基于最新的Java 21和SpringBoot 3.2 根据eladmin项目进行改造+源代码+文档说明

    - 使用最新技术栈,社区资源丰富,基于Java 21(Core Module Support 17-21)、Spring Boot 3.2。 (Support Virtual Threads/fibre/loom) - 基于注解的动态查询(Specification),可根据需要扩充查询注解。 - 支持...

    cache_server:缓存系统

    要点:使用RocksDB实现缓存持久化通过自定义序列化协议提升系统性能编写压力测试客户端对缓存系统进行压力测试利用管道技术提升客户端性能自定义序列化协议: 命令=操作键| 核心价值op ='S'| 'G'| 'D' 键=字节数组...

Global site tag (gtag.js) - Google Analytics