EHCache

Posted on

EHCache - 单落撒旦的日志 - 网易博客

帮助|留言交流| 登录

首 页 阅览室 馆友 我的图书馆

来自:软件123 > Hibernate

配色: 字号:大中小 EHCache - 单落撒旦的日志 - 网易博客 2010-12-07 | 阅:257 转:1 | 分享

Cache技术―OSCache

EHCache

缓存技术 2010-06-08 14:07:18 阅读170 评论0 字号:大订阅 一、简介

非常简单,而且易用。

ehcache 是一个非常轻量级的缓存实现,而且从1.2 之后就支持了集群,而且是hibernate 默认的缓存provider 。EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

Ehcache可以直接使用。也可以和Hibernate对象/关系框架结合使用。还可以做Servlet缓存。

Cache 存储方式 :内存或磁盘。

   官方网站:[http://ehcache.sourceforge.net/](http://ehcache.sourceforge.net/)

主要特性

  1. 快速.
  2. 简单.
  3. 多种缓存策略
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  5. 缓存数据会在虚拟机重启的过程中写入磁盘
  6. 可以通过RMI、可插入API等方式进行分布式缓存
  7. 具有缓存和缓存管理器的侦听接口
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
  9. 提供Hibernate的缓存实现
  10. 等等

二、快速上手

1、 项目类库中添加ehcache.jar;

2、 在类路径下编写ehcache.xml配置文件。

三、配置文件参数详解

ehcache.xml是ehcache的配置文件,并且存放在应用的classpath中。下面是对该XML文件中的一些元素及其属性的相关说明:

元素:指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下。 下面的参数这样解释:

     user.home – 用户主目录   

     user.dir      – 用户当前工作目录   

     java.io.tmpdir – 默认临时文件路径

元素:设定缓存的默认数据过期策略。

元素:设定具体的命名缓存的数据过期策略。 元素的属性 name:缓存名称。通常为缓存对象的类名(非严格标准)。 maxElementsInMemory:设置基于内存的缓存可存放对象的最大数目。 maxElementsOnDisk:设置基于硬盘的缓存可存放对象的最大数目。
eternal:如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false; timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态。 timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义。 overflowToDisk:如果为true,表示当基于内存的缓存中的对象数目达到了maxElementsInMemory界限后,会把益出的对象写到基于硬盘的缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。 memoryStoreEvictionPolicy:缓存对象清除策略。有三种: 1 FIFO ,first in first out ,这个是大家最熟的,先进先出,不多讲了 2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。 2 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 四、单独使用EHCache 1.创建CacheManager (net.sf.ehcache.CacheManager) (1)使用默认配置文件创建 CacheManager manager = CacheManager.create(); (2)使用指定配置文件创建 CacheManager manager = CacheManager.create("src/config/ehcache.xml"); (3)从classpath找寻配置文件并创建 URL url = getClass().getResource("/anothername.xml"); CacheManager manager = CacheManager.create(url); (4)通过输入流创建 InputStream fis = new FileInputStream(new File("src/config/ehcache.xml").getAbsolutePath()); try { manager = CacheManager.create(fis); } finally { fis.close(); } 2.创建Caches (net.sf.ehcache.Cache) (1)取得配置文件中预先 定义的sampleCache1设置,生成一个Cache Cache cache = manager.getCache("sampleCache1"); (2)设置一个名为test 的新cache,test属性为默认 CacheManager manager = CacheManager.create(); manager.addCache("test"); (3)设置一个名为test 的新cache,并定义其属性 CacheManager manager = CacheManager.create(); Cache cache = new Cache("test", 1, true, false, 5, 2); manager.addCache(cache); (4)删除cache CacheManager singletonManager = CacheManager.create(); singletonManager.removeCache("sampleCache1");
3.使用Caches (1)往cache中加入元素 Element element = new Element("key1", "value1"); cache.put(new Element(element); (2)从cache中取得元素 Element element = cache.get("key1"); (3)从cache中删除元素 Cache cache = manager.getCache("sampleCache1"); Element element = new Element("key1", "value1"); cache.remove("key1");
3.卸载CacheManager ,关闭Cache manager.shutdown(); 下附代码。 五、在 Hibernate 中运用EHCache 1、hibernate.cfg.xml中需设置如下: 3系列版本加入 org.hibernate.cache.EhCacheProvider EhCacheProvider类位于hibernate3.jar 2.1版本加入 net.sf.ehcache.hibernate.Provider 2.1以下版本加入 net.sf.hibernate.cache.EhCache 2、在Hibernate3.x中的etc目录下有ehcache.xml的示范文件,将其复制应用程序的src目录下(编译时会把ehcache.xml复制到WEB-INF/classess目录下),对其中的相关值进行更改以和自己的程序相适合。 3、持久化类的映射文件进行配置 标记中设置了,但Hibernate仅把和Group相关的Student的主键id加入到缓存中,如果希望把整个Student的散装属性都加入到二级缓存中,还需要在Student.hbm.xml文件的标记中加入子标记,如下所示: 注:SSH中hibernate配置的cache信息 org.hibernate.cache.EhCacheProvider 六、在页面中使用EHCache缓存 简单的来说,如果一个应用中80% 的时间内都在访问20% 的数据,那么,这时候就应该使用缓存了。 在80/20 原则生效的地方,我们都应该考虑是否可以使用缓存。但即使是这样,缓存也有不同的用法,举个例子,一个网站的首页估计是被访问的次数最多的,我们可以考虑给首页做一个页面缓存。页面访问最频繁的,做缓存。不同的页面的缓存策略有可能有天壤之别。 毫无疑问,几乎所有的网站的首页都是访问率最高的,而首页上的数据来源又是非常广泛的,大多数来自不同的对象,而且有可能来自不同的db ,所以给首页做缓存是一个不错的主意,那么主页的缓存策略是什么样子的呢,我认为应该是某个固定时间之内不变的,比如说2 分钟更新一次。或者根据不同的网页功能采取合理的策略。 在使用ehcache 的页面缓存之前,我们必须要了解ehcache 的2个概念: (1)timeToIdleSeconds ,多长时间不访问该缓存,那么ehcache 就会清除该缓存。 (2)timeToLiveSeconds ,缓存的存活时间,从开始创建的时间算起。 1、配置ehcache.xml文件 2、在web.xml配置文件中配置过滤器信息 好了,缓存整个页面看上去是非常的简单,甚至都不需要写一行代码,只需要几行配置就行了,够简单吧,虽然看上去简单,但是事实上内部实现却不简单哦,有兴趣的话,大家可以看看SimplePageCachingFilter 继承体系的源代码。 缓存首页(整个页面)示例: < filter > < filter-name > indexCacheFilter < filter-class > net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter
< filter-mapping > < filter-name > indexCacheFilter < url-pattern > /index.action 缓存首页的部分内容时,需要使用SimplePageFragmentCachingFilter 这个filter 。如:
< filter > < filter-name > indexCacheFilter < filter-class > net.sf.ehcache.constructs.web.filter.SimplePageFragmentCachingFilter
< filter-mapping > < filter-name > indexCacheFilter < url-pattern > /
/index_right.jsp
这个jsp 需要被jsp:include 到其他页面,这样就做到的局部页面的缓存。这一点貌似没有oscache 的tag 好用。 七、在Spring框架中使用EHCache缓存 就是使用Spring提供的springmodules和EHCache来简化程序的开发,通过配置文件来完成提供缓存。参考springmodules的文档。 1、配置ehcache.xml文件 2、创建Spring EHCache的配置xml文件 配置文件代码示例(调试通过): <?xml version="1.0" encoding="UTF-8"?> cacheName=dictCache cacheNames=dictCache /*anager flushingInterceptor cachingInterceptor

也可以使用注解的形式进行标注缓存方法,不过要修改配置文件,详见springmodules的文档,这里就不提供了。

缓存说明:

1、方法不含有参数

时间到期缓存失效;调用flush,缓存失效。

2、方法中含有参数

参数不同则每次都缓存,若缓存中存在相同对象,则调用缓存。

当调用flush,该id对应的缓存都失效。

当缓存时间到期,该id对应的缓存都失效。

建议:对没有关联的缓存对象采取不同的id配置。所以ehcache会有好多的cache-id配置信息。

       <props>

          <prop key="com....test.Manager.get/*">

              cacheName=dictCache

          </prop>

          ………                     

          <prop key="com....test.Manager2.get/*">

              cacheName=dictCache2

          </prop>

       </props>



       <props>

          <prop key="com....test.Manager.update/*">

              cacheNames=dictCache

          </prop>

          ………

          <prop key="com....test.Manager2.update/*">

              cacheNames=dictCache2

          </prop>

       </props>

上一篇:基于按annotation的hibernate主键生成策略 - 白首穷经通秘义...

下一篇:Cache技术―OSCache - 单落撒旦的日志 - 网易博客

献花(0)

+1 分享到:分享到QQ空间分享到新浪微博分享到腾讯微博分享到搜狐微博

类似文章

0

正在加载推荐文章

您可能会喜欢

[

老师开博客 天天开"网上家长会" ](http://www.360doc.com/content/07/0508/10/26144_488073.shtml "老师开博客 天天开"网上家长会"") [

博客冲击波(1)----商业周刊精华文章 ](http://www.360doc.com/content/08/0706/10/8596_1402791.shtml "博客冲击波(1)----商业周刊精华文章") [

红杏妹的博客 l ](http://www.360doc.com/content/12/0307/16/5060984_192513101.shtml "红杏妹的博客 l") [

Ehcache ](http://www.360doc.com/content/11/0811/15/7270363_139632250.shtml "Ehcache") [

教你怎样制作博客首页 - 敏儿的日志 - 网易博客 ](http://www.360doc.com/content/10/0317/13/826574_19113845.shtml "教你怎样制作博客首页 - 敏儿的日志 - 网易博客")

无觅

发表评论:

您好,请 登录 或者 注册 后再进行评论 使用合作网站登录:新浪微博QQ人人

孙中熙——路

馆藏:157 关注我:1

最新文章

热门文章

关闭

Web开发人员需知的Web缓存知识

Posted on

Web开发人员需知的Web缓存知识

原文出处: mnot 译文出处: 张鑫旭

最近的译文距今已有4年之久,原文有一定的更新。今天踩着前辈们的肩膀,再次把这篇文章翻译整理下。一来让自己对web缓存的理解更深刻些,二来让大家注意力稍稍转移下,不要整天HTML5, 面试题啊叨啊叨的~~

什么是Web缓存,为什么要使用它?

Web缓存游走于服务器和客户端之间。这个服务器可能是源服务器(资源所驻留的服务器Add),数量可能是1个或多个;这个客户端也可能是1个或多个。Web缓存就在服务器-客户端之间搞监控,监控请求,并且把请求输出的内容(例如html页面、 图片和文件)(统称为副本)另存一份;然后,如果下一个请求是相同的URL,则直接请求保存的副本,而不是再次麻烦源服务器。

使用缓存的2个主要原因:

  • 降低延迟:缓存离客户端更近,因此,从缓存请求内容比从源服务器所用时间更少,呈现速度更快,网站就显得更灵敏。
  • 降低网络传输:副本被重复使用,大大降低了用户的带宽使用,其实也是一种变相的省钱(如果流量要付费的话),同时保证了带宽请求在一个低水平上,更容易维护了。

Web缓存的类型

1. 浏览器缓存 在任何现代浏览器上(如IE, FireFox, Chrome)折腾清除隐私数据(//zxx: 原文说的是首选项,显然out了,这里有改动)的对话框,你很可能会注意到“缓存”这个设置项。

web制作、开发人员需知的Web缓存知识

浏览器会在你的硬盘上专门开辟一个空间专门为你存储资源副本。浏览器缓存的工作规则很简单:检查以确保副本是最新的,通常只要一次会话(就是当前浏览器调用的这次N)。

浏览器缓存在用户触发“后退”操作或点击一个之前看过的链接的时候很管用。同样,如果你在网站上访问同一张图片,该图片可以从浏览器缓存中调出并几乎立即显现出来。

2. 代理服务器缓存 Web代理服务器使用同样的缓存原理,只是规模更大。代理以同样的方式服务千万用户,大公司和ISP(Internet Server Provider, Internet服务提供商Add)经常在他们的防火墙或者单独的设备(也被称为中介(intermediaries))上架设代理缓存。

由于代理服务器缓存并非客户端或者源服务器的一部分,而是处于网络中,请求需要以某种方式路由到它们。一种方法是手动设置,告诉浏览器的你常用的代理服务器(//zxx: 翻墙的时候常用的),另外就是使用拦截。拦截代理(Interception proxies)把Web请求根据自己的底层网络重定向,因此,客户端无需配置,甚至都不需要知道它们。//zxx: 维基百科上提供的几种检测拦截代理服务器存在的方法add,您若有兴趣,可以点击这里查看。

代理缓存属于一种共享缓存;往往有大量的用户使用,因此,其在降低延时和网络流量上很有用,毕竟每个副本都被大量重用。//zxx: 这里我有疑问:就算是放在代理服务器上,每次获取还是要通过网络的啊,如何降低了网络流量呢?希望谁可以帮忙解惑下。

3. 网关缓存 也被称为“反向代理缓存”或“替代缓存”。网关缓存同样是起中介作用的,不过不是(素不相识、不曾谋面的Add)网络管理员部署的,而多半是网站管理员(公司专门的运维工程师、或UED或程序组某人Add)他们自己部署,这样更容易扩展与维护。

可以有多种方法把请求路由到网关缓存,但通常使用某种形式的负载均衡器①,使它们中的一个或多个看起来像是源服务器。内容分发网络②(CDNs)为整个网络(或部分)分配网关缓存,然后把这些缓存卖给需要的网站。Speedera③和Akamai④就是代表性的网络内容发布商。 ①负载均衡器:是一种采用各种分配算法把网络请求分散到一个服务器集群中的可用服务器上去,通过管理进入的Web数据流量和增加有效的网络带宽,从而使网络访问者获得尽可能最佳的联网体验的硬件设备。

②内容分发网络:即CDN, 基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。

③Speedera:是一家全球性的内容服务提供商,它与北美、欧洲以及亚太地区的1000多家大型运营商都有联系,并为那些不想在自己服务器上寄存内容的公司提供软件下载、媒体及其它服务管理等业务。05年的时候被下面要介绍的Akamai以$130m的价格给收购了。

④Akamai:美国Akamai是国际上最大的CDN服务商,它巨大的网络分发能力在峰值时可达到15Tbps。Akamai公司是为数不多的旨在消除Internet瓶颈和提高下载速度的几家新公司之一,是一个致力于网络交通提速的”内容发布”公司,是波士顿高技术区最卓越的新兴企业之一。Akamai公司向全球企业提供发送互联网内容,汇流媒体和应用程序的服务(目前,该公司为15个国家的企业管理着8000多台服务器)。1998年,丹尼尔。L和麻省理工学院的一些研究人员一起创立了这家公司,他在麻省理工学院的硕士论文构成了Akamai公司最初的”自由流”(Freeflow)技术的核心。

本教程重点在浏览器和代理缓存,尽管有些信息对网关缓存感兴趣的人也适用。

Web缓存无害吗?为什么要鼓励缓存?

Web缓存是互联网中最容易被误解的技术之一。网站管理员特别希望知道网站的一举一动,比方说多少人访问啦,访问时间啊什么的,而缓存会“隐藏”他们的用户,他们就无从得知到底谁访问了这个站点。

捡了芝麻丢西瓜,自认为放弃缓存可以精确跟踪用户,实际上,互联网中有太多的变数,想精确得到一张用户查看网站的图片?没那么简单的,亲!如果你很重视这个问题,恭喜你,本文正好提供了解决之道,即保证缓存友好,同时又能获得统计。

另外需要注意的是,缓存的内容都是旧的过时的。因此,如何准确更新就成了一个问题。不过不要担心,本文会向你展示如何配置服务器,让缓存就像你的女仆——随便调教。

web制作、开发人员需知的Web缓存知识

CDN算是个挺有意思的技术,不同于代理缓存,CDN的网关缓存和被缓存的Web站点的利益是一致的,因此,上面提到的问题对于CDN而言是没有的。不过,即使你使用了CDN,你仍要顾虑下游的代理和浏览器缓存。

以上为缓存可能的“糟粕”,那他好的地方呢?缓存可以让你的Web站点加载更快,让你的服务器和互联网链接间负担更小。这种差异会导致一些类似质的变化,一个网站要几秒钟才能加载出来,而另外一个充分发挥缓存的优势,几乎瞬间显示。用户自然更喜欢那个加载迅速的站点,访问也更多。

再说个现实示例,许多大型互联网公司花费了数百万美元,在世界各地设立服务器集群来复制他们的内容,以使其尽可能快被他们的用户访问。缓存为你做同样的事情,而且他们更接近最终用户。最重要的是,你不要花银子。

实际上呢,无论你喜欢与否,代理和浏览器缓存都会被使用。如果你站点的缓存配置不正确,你只能听天由命了。

Web缓存如何工作

所以的缓存都有一套自己的规则,可以用来决定何时跟缓存暧昧往来。其中部分规则设定在协议中(HTTP 1.0 以及 1.1),部分由缓存管理员⑤设置。 ⑤缓存管理员:如果指的是浏览器缓存,则有可能就是我们服务器专家同事,在服务器上配置一些缓存规则;如果是代理缓存,则指的就是处理代理服务器这块的管理人员。

一般而言有如下常用规则N:

  1. 响应头明确说明,偶不想被缓存,则不会被缓存;
  2. 如果请求信息是需要认证或者安全加密的(如, HTTPS),相应内容也不会被缓存;
  3. 缓存如果有以下表现,则认为是fresh新鲜的(无需检查源服务器,直接发送给客户端):
  • 含有完整的过期时间和寿命控制头信息,并且内容仍在保鲜期内,或者
  • 缓存最近已展现,并且在不久前修改。

则内容缓存直取,绕过源服务器。

  • 若内容陈旧,则会要求源服务器做验证 validate ,或者告诉缓存其拷贝副本是否是OK的。
  • 特定情况下——例如,断网了,之前有过的响应缓存直取而不检查源服务器。

响应如果没有类似ETagLast-Modified头这样的校验器,也没有明确的更新信息,通常(并不绝对)认为是不可缓存的。

总而言之,*新鲜度freshness和**校验*validation是确定缓存内容是否可用的最重要途径。如果要展示的足够新,直接缓存取;如果检测发现展示内容并未变化,则不会再来一次完整的传输。

如何控制缓存和不缓存

有很多工具可以帮助设计师和网站管理员调整服务器缓存网站的方式,这也许需要你亲自动手对服务器的配置进行一些调整,但绝对值得。了解如何使用这些工具请参考本文后面的章节。

HTML Meta标签 vs. HTTP头信息 HTML重构人员可以在文档的

中添加标签进行描述。这些meta标签通常用来标记不可缓存或过期时间。

Meta标签使用简单,但效果一般。因为只被少数几个浏览器宠幸,而代理缓存基本上就不访问HTML文档。尽管我们可以在页面上试图添加

no-cache meta标签让页面一直是最新的,但其实没必要。

如果你的网站托管在ISP或者主机托管商那里,并且他们没有赋予您任意设置HTTP头信息的能力(比如Expires和Cache-Control),你要投诉争取,因为在你的工作中这些是必须的。

另外一方面: HTTP头信息可以让你对浏览器和代理服务器如何处理你的副本进行更多的控制。他们在HTML代码中是看不见的,一般由Web服务器自动生成。但是,根据你使用的服务器,你可以在某种程度上进行控制。在下文中:你将看到一些有趣的HTTP头信息,以及如何在你的站点上应用部署这些特性。

HTTP头信息发送在HTML代码之前,只能被浏览器和一些中间缓存能看到,一个典型的HTTP 1.1协议返回的头信息看上去像这样: HTTP/1.1 200 OK

Date: Fri, 30 Oct 1998 13:19:41 GMT Server: Apache/1.3.3 (Unix)

Cache-Control: max-age=3600, must-revalidate Expires: Fri, 30 Oct 1998 14:19:41 GMT

Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT ETag: "3e86-410-3596fbbc"

Content-Length: 1040 Content-Type: text/html

头信息空一行后是HTML代码的输出,关于如何设置HTTP头信息请参考对应章节。

Pragma HTTP头信息(以及为什么不起作用) 很多人认为在HTTP头信息中设置了

Pragma: no-cache 后会让内容无法被缓存。但事实并非如此:HTTP的规范中,响应型头信息没有任何关于Pragma属性的说明,只说明了请求头信息(浏览器发送给服务器的头信息)中的Pragma属性。虽然有少部分缓存会买账,但大部分无视,使用Pragma没作用。若要使用,试试下面的头信息。

使用Expires HTTP头信息控制不过期 Expires HTTP头是控制缓存的基本手段,Expires的中文意思是“有效期”,显然,就是告诉浏览器缓存的有效期。如果过期,缓存会检查源服务器以确定文件是否改变了。Expires头几乎每个缓存都支持。

大部分的服务器允许你以多种方式设置Expires响应头。通常,他们允许设置一个绝对过期时间,然后对比最后一次访问的时候或者最后一次文档修改的时候决定客户端内容的获取方式。

对于静态图片(如导航或按钮的图片)而言,Expires头信息是相当有用的,因为图片不怎么修改,您可以给图片设置一个相当长的过期时间,这回让你的用户感觉网站变快了。Expires对于控制有改变规律的网页也很有用,例如:你有一个新闻聚合页面,每天早上6点钟准时更新,您可以设置缓存的过期时间也是这个点,于是缓存就可以很聪明地知道什么时候该去重载新的内容,什么时候睡大觉。

Expires头唯一的有效值是HTTP时间,其他值都会被认为是“前男友前女友”之类,不会去缓存的。注意:时间是格林威治时间(GMT),而不是本地时间。如下所示: Expires: Fri, 30 Oct 1998 14:19:41 GMT

显然,如果你要使用Expires头,确保你的Web服务器时间的准备就非常重要了。使用网络时间协议⑥(Network Time Protocol – NTP)不失为一个号方法。如果你的身边有本地系统管理员,可以向他咨询,或者查看下面的百科Add 。

尽管Expires头很有用,但它有一定的局限性。首先,因为牵扯到时间,Web服务器端的时钟必须和缓存的同步,否则很可能实现不了预期的结果——缓存把前女友当初现女友,把现女友当作过去式——那就悲剧了。

另外一个问题是,你很容易忘记给某内容设置了一个特定时间,如果返回内容的时候没有更新这个过期时间,则每个请求都是上访到服务器,反而增加了负载和响应时间。 ⑥网络时间协议(NTP): 以封包交换把两台电脑的时钟同步化的网络协议。NTP使用UDP端口123作为传输层。它是用作抵销可变延迟的影响。NTP是仍在使用中的最古老的网络协议之一(在1985年前开始)。NTP最初由德拉瓦州大学的Dave Mills设计,他与一群志愿者仍在维护NTP。

Cache-Control(缓存控制)HTTP头信息 HTTP 1.1引入了新的头信息:

Cache-Control 响应头信息,让网站的发布者可以更全面的控制他们的内容,更好地处理Expires的些限制。

Cache-Control 有用的响应头包括:

  • max-age=[秒]:表示在这个时间范围内缓存是新鲜的无需更新。类似Expires时间,不过这个时间是相对的,而不是绝对的。也就是某次请求成功后多少秒内缓存是新鲜的。
  • s-maxage=[秒]:类似

max-age , 除了仅应用于共享缓存(如代理)。

  • public:标记认证的响应才能够被缓存。一般而言,需要认证HTTP请求内容会自动私有化(不会被缓存Add)。
  • privateN:允许缓存专门为某一个用户存储响应,比方说在浏览器中;共享缓存一般不会,例如在代理中。
  • no-cache:每次在释放缓存副本之前都强制发送请求给源服务器进行验证,这在确保认证有效性上很管用(和

public 结合使用)或者保证内容必须是即时的,不得无视缓存的所有优点,如国内的微博、twitter等的刷新显示Add。

  • no-store:强制缓存在任何情况下都不要保留任何副本。
  • must-revalidate:告诉缓存,我给你准备了一些关于新鲜度的信息,在表现的时候要严格遵循之。HTTP允许缓存在某些特定情况下返回过期数据,指定了这个属性,相对于告诉缓存,你丫必须严格遵循我的规则。
  • proxy-revalidate:类似

must-revalidate ,除了只能应用于代理缓存。

举个板栗: Cache-Control: max-age=3600, must-revalidate

如果

Cache-Control 和

Expires 同时存在,

Cache-Control 说了算N。如果你打算使用

Cache-Control 头,你应该好好看看”HTTP 1.1 规范“, 详见参考文章以及拓展阅读。

验证器和验证 在缓存如何工作这段译文中,我们说过,服务器以及缓存通过验证来判断内容是否改变,在不确定内容是否过期的时候,可以避免本地已经存在副本的时候下载整个内容。

验证器是很重要的,如果一个都没有,同时没有可用的新鲜度信息(

Expires 或

Cache-Control ),缓存一点儿都不会存储内容。

最常见的验证是通过

Last-Modified 头信息通信确定文档最后的修改时间,如果缓存有内容存储,会包含

Last-Modified 信息的,辅助

If-Modified-Since 请求,我们可以询问服务器内容是否改变了。

HTTP 1.1引入了一个新的验证器,称为

Etag ⑦.

Etag 是每次展现内容改变时候由服务器生成的唯一标识符,由于服务器控制

ETag 如何生成,当缓存发起

If-None-Match 请求的时候,如果

Etag 匹配,就可以确定展示内容其实是一样的。 ⑦Etag: HTTP协议规格说明定义ETag为”被请求变量的实体值”。另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:ETag:”50b1c1d4f775c61:df3″客户端的查询更新格式是这样的:If-None-Match : W / “50b1c1d4f775c61:df3″如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。测试Etag主要在断点下载时比较有用。

几乎所有的缓存使用

Last-Modified 时间作为验证器,

Etag 验证也开始变得流行。

所有新一代的Web服务器都对静态内容(如:文件)自动生成

ETag 和

Last-Modified 头信息,而你不必做任何设置。但是,服务器对于动态内容(例如:CGI, ASP或数据库生成的网站)并不知道如何生成这些信息,参考一下编写支持缓存的脚本章节;

创建支持缓存网站的小技巧

除了使用新鲜度信息以及验证,还有其他一些技巧可以让你网站的缓存更加友好:

  • 保持URL稳定:这是缓存的金科玉律,如果你为不同页面,不同用户或不同网站提供相同的内容,他们应该使用相同的URL. 这是简单却非常行之有效的方法。例如,你的HTML中的某个引用地址是

"/index.html" , 则要一直使用这个地址。

  • 不同地方的图片和其他元素使用同一库
  • 对于不经常改变的图片/页面启用缓存,通过将

Cache-Control: max-age 头信息的值设大一点。

  • 对于定期更新的内容通过指定

max-age 或过期时间实现缓存。

  • 如果资源改变了(尤其下载文件),改变其名字。由于一般这种资源会有很长的过期时间,而服务器上一直是正确的版本;因此,链接这个下载资源的页面需要要比较短的过期时间(//zxx: 我司页面5分钟过期)。否则,会出现服务器的资源是新的,但页面被缓存了,其中的链接地址还是旧的,就会出现新旧版本冲突的可能Add。
  • 万不得已不要变动文件:否则你要设置一个新的

Last-Modified 值。另外,当你更新站点的时候,只要上传改动的那些文件,而不要把整个站点都覆盖过去。

  • Cookie能不用就不用:Cookie难以被缓存,且大多情境下是没有必要的。如果你非得使用Cookie,建议用在动态页面上。
  • 减少SSL⑧的使用:因为共享缓存不能存储认证页面,只在必要的时候使用,并且在SSL页面上减少图片的使用。
  • 使用REDbot⑨检查你的网站:可以帮助你应用本文所介绍的一些概念。 ⑧ SSL:全称Secure Socket Layer – 安全套接层,为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。目前一般通用之规格为40 bit之安全标准,美国则已推出128 bit之更高安全标准,但限制出境。只要3.0版本以上之I.E.或Netscape浏览器即可支持SSL。

⑨ REDbot:REDbot = RED + robot,是个机器人,检查HTTP资源,看他们如何会表现,指出常见的问题,并提出改进建议。虽然它属于HTTP一致性测试仪,但却可以找到不少HTTP相关问题。

编写支持缓存的脚本

默认情况下,大多数的脚本不会返回验证器(

Last-Modified 或

Etag 响应头)或新鲜度信息(

Expires 或

Cache-Control )。尽管有些脚本的确是动态的(意味着每次请求都有不同的响应),还是有很多(如搜索引擎或数据库驱动的)网站可以从缓存中受益。

一般来讲,对于同一个请求(无论是几分钟还是几天之后),如果脚本产生的内容是可重复的,则可以缓存。脚本内容的改变仅仅依赖于URL,则可以缓存。如果是依赖于Cookie,认证信息或其他外部条件,很可能不缓存。

  • 最利于缓存的脚本就是在内容改变时导出成静态文件,服务器会想对待其他Web一样对待它的,生成以及使用验证器,于是你可以好好地喝杯咖啡了。记住,只有文件更改的时候才写入,这样Last-Modified时间就会被保存下来。
  • 另外的脚本缓存之道就是使用

age 相关的头部,相比

Expires ,

Cache-Control: max-age 更容易些,因为是相对时间,每次新请求完成后重新设置,时间到了,再重新请求,再设置新的相对过期时间。

  • 如果上面的做法你搞不定,你还可以试试通过脚本生成一个校验器, 然后回应

If-Modified-Since 和/或

If-None-Match 请求。通过分析HTTP头信息,在适合的时候回应

304 Not Modified . 不幸的是,这不是个打打酱油就能搞定的任务。

其他一些技巧

  • 不要使用POST:若是获取数据,尽量不使用POST模式,因为POST方式返回内容大部分不会被缓存,相对的,通过GET以路径和查询发送的信息被缓存存储下来供后续使用。
  • URL地址中不要嵌入特定的用户信息,除非生成的内容对于用户而言是唯一的。
  • 不要指望同一用户的所有请求来自同一主机,因为缓存经常协同工作。//zxx: 嘛意思?
  • 生成Content-Length⑩头信息。实现不难,可让你的脚本以持久连接(persistent connection)形式响应。这允许客户端在一个TCP/IP请求上请求多个内容,而不是为每次请求单独建立连接,这样你的网站相应会快很多。

详见实现注意事项。 ⑩Content-Length:指明实体正文的长度,以字节方式存储的十进制数字来表示。在数据下行的过程中,Content-Length的方式要预先在服务器中缓存所有数据,然后所有数据再一股脑儿地发给客户端。

常见问题解答

缓存可用的最重要事情是? 其中一个不错的策略是找出常用的、规模较大的内容(尤其图片),然后优先处理之。

我该如何利用缓存让我的页面尽可能的快? 最应该缓存的内容设置一个较长的过期时间。验证有助于减少查看内容的时间,不过缓存仍会连接源服务器查看是不是过期了。如果缓存已经知道内容是新鲜的,直接返回。

我知道缓存是个好东西,但是我想随时知道多少人访问了我的网页! 如果你必须知道每一次页面被访问的情况,可以选择页面上的一个小元素(或页面本身),然后给这个元素一个适当的头信息使它是不可缓存。比如,你可以在每一个页面上引用一个1像素×1像素的不可缓存(如scr地址后面加个随机数Add)的透明图片。Referer头信息将会包含调用它的页面信息。

请注意,即使这样也不能给出你用户的精确统计,并且对通过互联网访问的用户也不是很友好:产生不必要的流量,并强迫用户等待未被缓存的内容从网络上下载回来。更多的信息可参见拓展阅读中的“解读访问统计”对应内容。

我该如何查看HTTP头? 许多浏览器可以查看

Expires 和

Last-Modified 头信息,如右键→查看页面信息或类似面板。例如,在Firefox浏览器下Add:

web制作、开发人员需知的Web缓存知识web制作、开发人员需知的Web缓存知识

表示要看到完整的头,您可以使用Telnet⑪客户端手动连接到Web服务器上。

为此,你可能需要用一个字段指定端口(默认是80),或者连接到

www.example.com:80 或者

www.example.com 80 (注意是空格),更多设置请参考一下telnet客户端的文档。

一旦连接到该网站,输入请求。比如,你想查看

http://www.example.com/foo.html 的头信息,首先连接到

www.example.com , 使用80端口,并输入: GET /foo.html HTTP/1.1 [return]

Host: www.example.com [return][return]

[return] 等同敲回车键,最后输入两次确认。这样就会输出头信息,然后跟着实际内容。如果只想看到头信息,使用

HEAD 来替换

GET .

⑪Telnet:Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。

我的页面是密码保护的,代理缓存是怎么处理的? 默认情况下,HTTP验证保护的页面是私有的,共享缓存是不能保存的。然而,你可以通过

Cache-Control: public 头的设置使其公有。HTTP 1.1标准兼容的缓存服务器可以使之缓存。

如果你希望这些缓存的页面在用户查看之前还要验证一下,可以组合使用

Cache-Control: public 和

no-cache 头,这相对于告诉缓存器它从缓存中送出内容前必须递交客户端的验证给原始服务器。这个头信息如下所示: Cache-Control: public, no-cache

不管怎么,这是最小化验证最好的方法;例如,你的图片不敏感,你可以把它放在分离的目录中,并配置你的服务对它们不做强制验证。这样,那些图片就会很自然的被缓存了。

如果人们通过缓存访问我的网站,我应该担心安全吗? SSL页面不会被代理服务器缓存,所以这个你不需要担心。但是,代理服务器就好非SSL页面请求以及URL抓取这口,你懂的,这是不安全的。无良的管理员可能就会收集网站用户的信息,尤其在URL中。

事实上,任何网络管理员都可以收集你的客户端和服务器端之间的这类信息。CGI ⑫脚本有个漏洞,会把用户名和密码放在自身的URL地址中,这很容易让其他人发现用户的登陆信息。

如果你懂得互联网安全的些基本机制,就不会对代理缓存感到任何惊讶。 ⑫CGI:通用网关接口(Common Gateway Interface). 用于初始化软件服务的服务器方接口。这套接口描述了Web服务器与同一计算机上的软件的通信方式。

通用网关接口,它是一段程序,运行在服务器上,提供同客户端HTML页面的接口,通俗的讲CGI就像是一座桥,把网页和WEB服务器中的执行程序连接起来,它把HTML接收的指令传递给服务器,再把服务器执行的结果返还给HTML页;用CGI可以实现处理表格,数据库查询,发送电子邮件等许多操作,最常见的CGI程序就是计数器。CGI使网页变得不是静态的,而是交互式的。

我在寻找一个集成的Web发布解决方案。哪些是可缓存的? 这个是不确定的。一般来说,越复杂的系统越难缓存。最差的情况就是所有的内容都是动态生成,并且不提供校验器,与缓存压根无缘。你可以和你供应商的技术人员沟通获取更多信息,并参考下面实现注意事项。

我的图片缓存一个月后才到期,我现在就想变动!

Expires 头是绕不过去的,除非缓存(浏览器或者代理)空间不足才会删除副本,缓存副本会一直使用。

最有效的方法是修改链接,这样会从源服务器获取完整的新内容。请记住,调用图片的这个页面也会被缓存的,正因如此,我们需要让图片以及其他类似的静态资源易缓存,而页面呢可以随着自身的改变(例如改变了一个图片的URL地址Add)即时更新。

如果你想摆脱特定缓存,重载内容,可以试试强制刷新(在FireFox中,shift键+reload按钮等同于处理

Pragma: no-cache 请求头)或者让缓存管理员使用某些接口删除内容。

我运行一个Web Hosting服务。我怎样才能让我的用户发布缓存友好的网页? 如果你使用apahe,可以考虑允许他们使用

.htaccess 文件并提供相应的文档。

否则你需要在每一个虚拟主机上为各种缓存属性建立预定的区域。比如:你可以指定一个叫

/cache-1m 的目录用来放读取后要缓存一个月的内容,然后再建一个

/no-cache 的目录,并在头信息中指定这么目录中的内容不被缓存。

不管上面你做的如何,总之最好优先给用户量大的客户做缓存处理。大部分服务器节约的流量以及负载都是来自高容量的网站。

我明明告诉网页要好好缓存,但它老是去请求,怎么破? 缓存服务器并不总是要求内容要保持并重用,某些条件下,他们是不保存不重用的,所有的缓存服务器都回基于文件的大小、类型(图片、页面…),或者服务器空间的剩余来确定如何缓存。如果你的文件比较大或很热门,可能就不会被缓存。有些缓存服务器允许管理员决定哪些内容要存储,有些缓存服务器允许内容长存缓存中,所以,它们总是可用的。

实现需注意的:Web服务器端

一般说来,应该选择最新版本的Web服务器程序来部署。不仅因为它们包含更多利于缓存的功能,新版本往往在性能和安全性方面都有很多的改善。

Apache HTTP服务器 Apache使用可选模块包含头信息,头信息

Expires 和

Cache-Control 一并包含。这些模块在1.2版本以上都支持。

这些模块需要编译到Apache中,虽然包含,但是默认并未开启。为了确定相应模块已经被启用,找到httpd⑬程序,运行

httpd -l , 它会列出可用的模块(注意,仅有内部编译的模块列表才会显示,在较新版本的Apache中,使用

httpd -M 可以包含动态加载的模块N),我们需要关注的是expires模块(

expires_module )和headers模块(

headers_module )。 ⑬httpd:httpd是Apache超文本传输协议(HTTP)服务器的主程序。被设计为一个独立运行的后台进程,它会建立一个处理请求的子进程或线程的池。

  • 如果这些模块不可用,你需要联系管理员,重新编译以包含这些模块。这些模块可以通过取消配置文件中的注释掉启用,或者在编译的时候增加

-enable -module=expires 和

-enable-module=headers 参数(apache 1.3+). 参开Apache中的INSTALL文件。

一旦你的Apache有了相应的模块,你可以使用

mod_expires 指定过期的时间,要么在

.htaccess 文件,要么在服务器的

access.conf 文件。你可以设置过期时间是从访问时间开始还是文件修改时间开始,并应用到特定类型文件上或设为默认配置。查看官方该模块文档获得更多信息,或者遇到问题的时候向你身边的apache专家讨教。

为应用

Cache-Control 头,你需要使用

mod_headers 模块,其允许你为资源指定任意的头信息。可参考mod_headers官方文档

下面是

.htaccess 文件展示了如何使用头信息:

  • .htaccess 文件允许Web发布者使用配置文件中的指定。可以影响目录以及子目录内容。和你的服务器管理员沟通下,看看它们是否可用。

/#/#/# activate mod_expires

ExpiresActive On /#/#/# Expire .gif's 1 month from when they're accessed

ExpiresByType image/gif A2592000 /#/#/# Expire everything else 1 day from when it's last modified

/#/#/# (this uses the Alternative syntax) ExpiresDefault "modification plus 1 day"

/#/#/# Apply a Cache-Control header to index.html

Header append Cache-Control "public, must-revalidate"

  • 注意,在有些情况下,

mod_expires 会自动计算并插入

Cache-Control:max-age 头信息。

Apache 2′s的配置和1.3类似,更多信息可以参考2.2N的mod_expiresmod_headers文档。

微软IIS 微软的IIS有一些灵活的方式可以很容易得设置头信息,不过似乎只针对IIS 4.0服务器,并且只能在NT服务器上运行。

为了给网站某区域指定头信息,需要进入

Administration (管理员)工具面板,然后再设置属性。选择

HTTP Headers 选项卡后,你会看到两个有意思的区域:

Enable Content Expiration 和

Custom HTTP headers , 第一个含义一目了然,第二个用来应用

Cache-Control 头。//zxx: 此处的操作描述很过时了,看看window7下,操作界面早就大变样了!

web制作、开发人员需知的Web缓存知识web制作、开发人员需知的Web缓存知识

设置ASP页面(Active Server Pages)的头信息可以参考后面的ASP章节,也可以通过ISAPI模块设置头信息,细节请参考MSDN。

Netscape/iPlanet企业服务器 3.6版本以后,企业版服务器已经不能以任何方式设置

Expires 头信息了。然而,其从3.0版本开始支持HTTP 1.1的功能。这意味着HTTP 1.1的缓存(代理服务器/浏览器)利用你对

Cache-Control 的设置来获得。

为了使用

Cache-Control 头,在管理员服务器中选择

Content Management | Cache Control Directives (内容管理|缓存控制指令)。然后,使用资源选择器(Resource Picker),选择你希望设置头信息的目录。设置完头信息后,点击”确定”。更多信息请参考NES手册

实现需注意的:服务端脚本

时刻谨记,在Web服务器上设置HTTP要比通过脚本设置轻松些。你可以两者都试试。

因为服务器端的脚本主要是为了动态内容,所以即使实际上内容可以被缓存的,其也不会生成缓存很强的页面。如果你的页面内容经常变动,但不是每个页面都中枪,可以考虑设置

Cache-Control: max-age 头信息,大部分用户是在相对端的时间内再次访问这个页面。例如:用户点击“后退”按钮,如果没有任何验证或新鲜度信息,他们将不得不等待,直到从服务器页面重新下载才能看到它。

CGI CGI脚本是生成内容最常用的技术之一。你可以轻轻松松在请求发送给主体之前添加HTTP请求信息。大部分CGI实现都需要添加

Content-Type 头信息,例如这个Perl脚本://zxx: 还是挺好懂的 </#!/usr/bin/perl

print "Content-type: text/htmln"; print "Expires: Thu, 29 Oct 1998 17:04:19 GMTn";

print "n"; /#/#/# the content body follows.../pre>

由于都是文本,你可以很容易通过内置函数生成

Expires 和其他日期相关的头信息。如果你使用

Cache-Control: max-age 会更简单:

print "Cache-Control: max-age=600n";

上面脚本可以让请求完成后缓存10分钟,因此,当用户点击“后退”按钮的时候,就不会重新涂胶请求了。

CGI的规范同时也允许在脚本环境中,客户端发送请求头信息,每个头信息都有一个’HTTP_’的前缀。于是乎,如果一个客户端发送一个

If-Modified-Since 请求,就是这样的: HTTP_IF_MODIFIED_SINCE

可观摩cgi_buffer库,其可以自动实现

Etag 生成和验证,

Content-Length 生成及gzip内容,而所有这些实现,只需要一行include,就可以为Perl和Python写CGI脚本。Python版本还可以包装任意的CGI脚本。

服务器端包含 SSI(扩展名通常是

.shtml )最早可以生成动态内容的网站发布方案。通过在页面中使用特定的标签,有一定限制的内HTML脚本就可以使用了。大部分的SSI实现不设置验证器,故无法缓存。不过Apache服务器允许通过设置让SSI文件可缓存,通过适当的文件并结合

XbitHack full 指令设置组执行权限。欲了解更多信息,请参阅mod_include文档

PHP PHP为服务器端脚本语言,在服务器内置的时候,可以在HTML页面中内嵌使用,很像SSL,不过有更多的可选项。PHP可以在任何Web服务器(Unix或Windows)或Apache模块上作为CGI使用。

默认情况下,PHP生成的内容没有分配验证器,因此,不能缓存。不过,开发人员可以通过

Header() 函数设置HTTP头信息。例如,创建

Cache-Control 头,过期时间为3天: <?php

Header("Cache-Control: must-revalidate");

$offset = 60 / 60 / 24 /* 3; $ExpStr = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";

Header($ExpStr); ?>

记住

Header() 需要在所有的输出之前。

正如你看到的,你可以手工创建HTTP日期。PHP没有专门的函数(新版本已改进,请参考PHP的日期相关函数文档)。当然,最简单的还是设置

Cache-Control: max-age 头信息,适用于大部分情况。

更多内容,请参阅header手册

还是cgi_buffer库,只要一行包含,就能以PHP脚本形式自动实现

Etag 生成和验证,

Content-Length 生成及gzip内容。

Cold Fusion Cold Fusion是Macromedia的商业服务器端脚本引擎,并且支持多种Windows平台,Linux平台和多种Unix平台。//zxx: 看到Macromedia心亮了半截,几百年前就被收购的公司……此文未免太过时了点了~~大家这段可以跳过了,几乎没有任何价值……

Cold Fusion通过CFHEADER标记设置HTTP头信息相对容易。可惜的是,以下的Expires头信息的设置有些容易误导:

它并不像你想像的那样工作,因为时间(本例中为请求发起的时间)并不会被转换成一个符合HTTP时间,而且打印出副本的Cold fusion的日期/时间对象,大部分客户端会忽略或者将其转换成1970年1月1日。

但是:Cold Fusion另外提供了一套日期格式化函数-GetHttpTimeSTring. 结合DateAdd函数,就很容易设置过期时间了,这里我们设置一个头信息,声明内容在1个月以后过期:

你也可以使用

CFHEADER 标签设置

Cache-Control: max-age 以及其他头信息。

记住,Web服务器也会将头信息设置转给Cold Fusion(做为CGI运行的时候),检查你的服务器设置并确定你是否可以利用服务器设置代替Cold Fusion.

ASP和ASP.NET

在asp中设置HTTP头信息时,确保

Response 方法调用在HTML内容输出之前,或者使用

Response.Buffer 暂存输出。同时,注意某些版本的IIS默认设置会输出

Cache-Control: private 头信息,必须声明成

public 才能被共享缓存服务器缓存。

ASP(Active Server Pages),IIS内置,也可用于其他Web服务器,同样允许你设置HTTP头。例如设置过期时间,你可以使用

Response 自带属性: <% Response.Expires=1440 %>

指定内容过期的分钟数。

Cache-Control 头添加如下:

<% Response.CacheControl="public" %>

在ASP.NET中,

Response.Expires 已经不推荐使用了,正确的方法是通过

Response.Cache 设置缓存相关的头信息,如下:

Response.Cache.SetExpires ( DateTime.Now.AddMinutes ( 60 ) ) ;

Response.Cache.SetCacheability ( HttpCacheability.Public ) ;

参考文档和拓展阅读

HTTP 1.1规范 HTTP 1.1的规范对页面缓存以及权威的接口实现指南有了大量的扩展,参考章节:13, 14.9, 14.21以及14.25 .

Web-Caching.com 对缓存概念有很好的介绍,并且有很多其他在线资源的链接。

解读访问统计 Jeff Goldberg这篇内容丰富叙述会告诉你为什么不应该过度依赖访问统计和计数器。//zxx 上世纪的复古页面…

REDbot 检查HTTP资源,以确定它们如何与Web缓存交互,以及通常如何使用该协议。

cgi_buffer库 只要包含一行Perl CGI, Python CGI以及PHP脚本,就能自动实现

Etag 生成以及验证,

Content-Length 生成以及Gzip内容的正确编码。Python版本还可以包装任意的CGI脚本。

关于本文档

本文版权属于Mark Nottingham © 1998-2013 邮箱为

mnot@pobox.com .

本作品遵循知识共享署名 – 非商业性使用 – 禁止演绎3.0声明页面许可证N。

所有的商标版权为其持有人所有。

内容在发布时是可以确保其正确性,但是,随着时间推移,就不能保证正确无误了。因此,如有链接404,描述错误或其他需要纠正的问题请尽快告知作者。

本文最新版本可以从http://www.mnot.net/cache_docs/获得

可用翻译:白俄罗斯中国捷克德国法国

文档说明:含有上标N的表示与前辈翻译时候相比新增的;上标Add表示作为译者的我为了便于理解自己添加的;上标数字(①-⑬)是对一些可能不熟悉的名词的百科解释。

虽然原作语言不生动,教科书般一板一眼⑭;有些可能过时了。不过,还是学到了很多东西。还是很值的!欢迎分享,欢迎传播,以后面试之前来这里看看,可能会有帮助哦! ⑭ 如果我介绍缓存,我可能就这么讲:缓存是什么?顾名思意,就是缓慢的存钱。为什么要缓慢的存钱,因为工资卡都上交老婆了,为了攒点零花钱又不能被老婆发现,只能慢慢存了。那缓存有什么用呢?你想啊,自己有点小钱,做事情就方便快捷了,比方说我想买个鱼竿,就不要去向老婆要(给不给先不谈),自己从自己这边取,大大提高了执行的速度。

那什么时候可以存什么时候不能存呢?老婆给零花钱的时候,可能会有过期时间头,例如,周一甩了100块钱,拿去,这是一周的伙食!这个一周就是过期时间头(Expires Header),在这个时间内,你的钱可以从缓存,也就是自己这里取……

1 vote, average: 5.00 out of 51 vote, average: 5.00 out of 51 vote, average: 5.00 out of 51 vote, average: 5.00 out of 51 vote, average: 5.00 out of 5 (*1 个评分,平均: 5.00*)

Loading ... Loading ...

EhCache小结

Posted on

EhCache小结 - - ITeye技术网站

首页 资讯 精华 论坛 问答 博客 专栏 群组 更多 ▼

招聘 搜索

您还未登录 ! 登录 注册

nanguocoffee

EhCache小结 **

博客分类:

一:接口和类的作用: Elemenet: key value lastAccessTime hitCount 等描述cache中元素的信息。 Store: 实际上存放Element的对象,Cache针对对象的操作一般都委托Store对象。

  • MemoryStore: 继承该类来实现自定义MemoryStore。该类存放数据时,先使用Map存放数据,然后再判断是否超出容量。 put() doPut() // 用来清理元素 -LruMemoryStore.java 使用 LinkedHashMap的子类,doput()为空,因为该map的实现中的removeEldestEntry方法清理了超出容量的元素。 -LfuMemoryStore.java 使用HashMap -FifoMemoryStore.java 使用LinkedHashMap 这里用队列应该更快吧??源码中还需要遍历map。 -DiskStore: 其实diskStore也是用一个hashmap来存放数据,只有显式的调用flush()方法的时候才判断是否要将元素写入文件中。 CacheManager 来管理 Cache ; Cache用来操作Element,Cache是抽象出来的逻辑概念,其 内部使用Store来处理Element Element就是数据描述缓存对象的基本信息。 Store是保管缓存对象的仓库,这个仓库分Memory和Disk两种。 EHCache中的EventListener是针对分布式的,普通的应用可以不用考虑。 二:一些功能细节: 1:超出设定的元素容量的解决方法 MemoryStore: When an element is added to a cache and it goes beyond its maximum memory size, an existing element is either deleted, if overflowToDisk is false, or evaluated for spooling to disk, if overflowToDisk is true. In the latter case, a check for expiry is carried out. If it is expired it is deleted; if not it is spooled. 判断元素是否过期,MemoryStore有3种策略可选,LRU,LFU,FIFO,默认为LRU。 DiskStore: 使用LFU,DiskStore使用一个守护线程,DiskStore.SpoolAndExpiryThread内部类来定时检查diskStore中的元素, 定时刷新到文件中,如果遇到过期的元素则移除。 2:元素存活时间到期 MemoryStore: 在get()和超出容量的时候才判断是否过期 DiskStore: 只有在flush的时候才判断是否过期,如果过期则移除。 也就是说,这两种方式,不论在文件或者内存中,都会依然存在过期的元素, 这些元算可能永远存在,但不影响我们的操作,因为我们get()或者超出容量的时候都会判断是否过期并清除。 三:初始化 CacheManager cacheManager = new CacheManager(); 调用protected void init(Configuration configuration, String configurationFileName, URL configurationURL, InputStream configurationInputStream) 方法进行初始化。 1:解析配置信息: 1.1 查找ehcache.xml文件,如果找不到则使用默认的ehcache-failsafe.xml,返回一个URL对象。 1.2 调用ConfigurationFactory.parseConfiguration(input) 方法来进行解析。返回Configuration对象。 Configuration的数据结构: private DiskStoreConfiguration diskStoreConfiguration; private CacheConfiguration defaultCacheConfiguration; private List cacheManagerPeerProviderFactoryConfiguration = new ArrayList(); private List cacheManagerPeerListenerFactoryConfiguration = new ArrayList(); private FactoryConfiguration cacheManagerEventListenerFactoryConfiguration; private final Map cacheConfigurations = new HashMap(); private String configurationSource; 该数据结构是针对xml定义来的,其中最复杂也最常用的算是CacheConfiguration,其内部结构也很简单。 cacheConfigurations存放多个CacheConfiguration。 2:根据Configuration进行CacheManager,Cache,Store等数据结构的初始化。 总结: EHCache是一款比较简单的开源缓存产品,但也存在一些不足之处: 比如: 1:CacheManager和Cache这个具体类耦合,当然也提供了针对EHCache接口的方法,如果使用自定义的Cache的话容易混淆方法。 2:针对不同的Cache类,我们希望指定不同的MemoryStore实现、DiskStore实现,EHCache只提供了通过程序来达到这一功能, 没有提供通过配置文件来提供(类似于Hibernate配置文件中CacheProvider,JDBC dialect 等)。 3:有些算法实现得不算好,比如FifoMemoryStore.java中的清理元素算法选择的数据结构不够快速。 分享到:

JForum源码学习研究1-起步 | EhCache入门

评论

发表评论

您还没有登录,请您登录后再发表评论

NanguoCoffee的博客

NanguoCoffee

  • 浏览: 10206 次
  • 性别: Icon_minigender_1
  • 来自: 北京
  • 最近访客 更多访客>>

bingki的博客

bingki

Turandot的博客

Turandot dylinshi126的博客

dylinshi126

luwies的博客

luwies

文章分类

存档分类

Ehcache详细解读

Posted on

Ehcache详细解读 - 四火的BLOG - ITeye技术网站

首页 资讯 精华 论坛 问答 博客 专栏 群组 更多 ▼

招聘 搜索

您还未登录 ! 登录 注册

四火的BLOG

Ehcache详细解读 **

博客分类:

Ehcache 是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大,最初知道它,是从Hibernate的缓存开始的。网上中文的EhCache材料以简单介绍和配置方法居多,如果你有这方面的问题,请自行google;对于API,官网上介绍已经非常清楚,请参见官网;但是很少见到特性说明和对实现原理的分析,因此在这篇文章里面,我会详细介绍和分析EhCache的特性,加上一些自己的理解和思考,希望对缓存感兴趣的朋友有所收获。

一、特性一览,来自官网,简单翻译一下:

1、快速轻量 过去几年,诸多测试表明Ehcache是最快的Java缓存之一。 Ehcache的线程机制是为大型高并发系统设计的。 大量性能测试用例保证Ehcache在不同版本间性能表现得一致性。 很多用户都不知道他们正在用Ehcache,因为不需要什么特别的配置。 API易于使用,这就很容易部署上线和运行。 很小的jar包,Ehcache 2.2.3才668kb。 最小的依赖:唯一的依赖就是SLF4J了。 2、伸缩性 缓存在内存和磁盘存储可以伸缩到数G,Ehcache为大数据存储做过优化。 大内存的情况下,所有进程可以支持数百G的吞吐。 为高并发和大型多CPU服务器做优化。 线程安全和性能总是一对矛盾,Ehcache的线程机制设计采用了Doug Lea的想法来获得较高的性能。 单台虚拟机上支持多缓存管理器。 通过Terracotta服务器矩阵,可以伸缩到数百个节点。 3、灵活性 Ehcache 1.2具备对象API接口和可序列化API接口。 不能序列化的对象可以使用除磁盘存储外Ehcache的所有功能。 除了元素的返回方法以外,API都是统一的。只有这两个方法不一致:getObjectValue和getKeyValue。这就使得缓存对象、序列化对象来获取新的特性这个过程很简单。 支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。 提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。 提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。 动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。 4、标准支持 Ehcache提供了对JSR107 JCACHE API最完整的实现。因为JCACHE在发布以前,Ehcache的实现(如net.sf.jsr107cache)已经发布了。 实现JCACHE API有利于到未来其他缓存解决方案的可移植性。 Ehcache的维护者Greg Luck,正是JSR107的专家委员会委员。 5、可扩展性 监听器可以插件化。Ehcache 1.2提供了CacheManagerEventListener和CacheEventListener接口,实现可以插件化,并且可以在ehcache.xml里配置。 节点发现,冗余器和监听器都可以插件化。 分布式缓存,从Ehcache 1.2开始引入,包含了一些权衡的选项。Ehcache的团队相信没有什么是万能的配置。 实现者可以使用内建的机制或者完全自己实现,因为有完整的插件开发指南。 缓存的可扩展性可以插件化。创建你自己的缓存扩展,它可以持有一个缓存的引用,并且绑定在缓存的生命周期内。 缓存加载器可以插件化。创建你自己的缓存加载器,可以使用一些异步方法来加载数据到缓存里面。 缓存异常处理器可以插件化。创建一个异常处理器,在异常发生的时候,可以执行某些特定操作。 6、应用持久化 在VM重启后,持久化到磁盘的存储可以复原数据。 Ehcache是第一个引入缓存数据持久化存储的开源Java缓存框架。缓存的数据可以在机器重启后从磁盘上重新获得。 根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.flush()方法来执行,这大大方便了Ehcache的使用。 7、监听器 缓存管理器监听器。允许注册实现了CacheManagerEventListener接口的监听器: notifyCacheAdded() notifyCacheRemoved() 缓存事件监听器。允许注册实现了CacheEventListener接口的监听器,它提供了许多对缓存事件发生后的处理机制: notifyElementRemoved/Put/Updated/Expired 8、开启JMX Ehcache的JMX功能是默认开启的,你可以监控和管理如下的MBean: CacheManager、Cache、CacheConfiguration、CacheStatistics 9、分布式缓存 从Ehcache 1.2开始,支持高性能的分布式缓存,兼具灵活性和扩展性。 分布式缓存的选项包括: 通过Terracotta的缓存集群:设定和使用Terracotta模式的Ehcache缓存。缓存发现是自动完成的,并且有很多选项可以用来调试缓存行为和性能。 使用RMI、JGroups或者JMS来冗余缓存数据:节点可以通过多播或发现者手动配置。状态更新可以通过RMI连接来异步或者同步完成。 Custom:一个综合的插件机制,支持发现和复制的能力。 可用的缓存复制选项。支持的通过RMI、JGroups或JMS进行的异步或同步的缓存复制。 可靠的分发:使用TCP的内建分发机制。 节点发现:节点可以手动配置或者使用多播自动发现,并且可以自动添加和移除节点。对于多播阻塞的情况下,手动配置可以很好地控制。 分布式缓存可以任意时间加入或者离开集群。缓存可以配置在初始化的时候执行引导程序员。 BootstrapCacheLoaderFactory抽象工厂,实现了BootstrapCacheLoader接口(RMI实现)。 缓存服务端。Ehcache提供了一个Cache Server,一个war包,为绝大多数web容器或者是独立的服务器提供支持。 缓存服务端有两组API:面向资源的RESTful,还有就是SOAP。客户端没有实现语言的限制。 RESTful缓存服务器:Ehcached的实现严格遵循RESTful面向资源的架构风格。 SOAP缓存服务端:Ehcache RESTFul Web Services API暴露了单例的CacheManager,他能在ehcache.xml或者IoC容器里面配置。 标准服务端包含了内嵌的Glassfish web容器。它被打成了war包,可以任意部署到支持Servlet 2.5的web容器内。Glassfish V2/3、Tomcat 6和Jetty 6都已经经过了测试。 10、搜索 标准分布式搜索使用了流式查询接口的方式,请参阅文档。 11、Java EE和应用缓存 为普通缓存场景和模式提供高质量的实现。 阻塞缓存:它的机制避免了复制进程并发操作的问题。 SelfPopulatingCache在缓存一些开销昂贵操作时显得特别有用,它是一种针对读优化的缓存。它不需要调用者知道缓存元素怎样被返回,也支持在不阻塞读的情况下刷新缓存条目。 CachingFilter:一个抽象、可扩展的cache filter。 SimplePageCachingFilter:用于缓存基于request URI和Query String的页面。它可以根据HTTP request header的值来选择采用或者不采用gzip压缩方式将页面发到浏览器端。你可以用它来缓存整个Servlet页面,无论你采用的是JSP、velocity,或者其他的页面渲染技术。 SimplePageFragmentCachingFilter:缓存页面片段,基于request URI和Query String。在JSP中使用jsp:include标签包含。 已经使用Orion和Tomcat测试过,兼容Servlet 2.3、Servlet 2.4规范。 Cacheable命令:这是一种老的命令行模式,支持异步行为、容错。 兼容Hibernate,兼容Google App Engine。 基于JTA的事务支持,支持事务资源管理,二阶段提交和回滚,以及本地事务。 12、开源协议 Apache 2.0 license

二、Ehcache的加载模块列表,他们都是独立的库,每个都为Ehcache添加新的功能,可以在此下载

  • ehcache-core:API,标准缓存引擎,RMI复制和Hibernate支持
  • ehcache:分布式Ehcache,包括Ehcache的核心和Terracotta的库
  • ehcache-monitor:企业级监控和管理
  • ehcache-web:为Java Servlet Container提供缓存、gzip压缩支持的filters
  • ehcache-jcache:JSR107 JCACHE的实现
  • ehcache-jgroupsreplication:使用JGroup的复制
  • ehcache-jmsreplication:使用JMS的复制
  • ehcache-openjpa:OpenJPA插件
  • ehcache-server:war内部署或者单独部署的RESTful cache server
  • ehcache-unlockedreadsview:允许Terracotta cache的无锁读
  • ehcache-debugger:记录RMI分布式调用事件
  • Ehcache for Ruby:Jruby and Rails支持

Ehcache的结构设计概览:

三、核心定义

cache manager:缓存管理器,以前是只允许单例的,不过现在也可以多实例了

cache:缓存管理器内可以放置若干cache,存放数据的实质,所有cache都实现了Ehcache接口

element:单条缓存数据的组成单位

system of record(SOR):可以取到真实数据的组件,可以是真正的业务逻辑、外部接口调用、存放真实数据的数据库等等,缓存就是从SOR中读取或者写入到SOR中去的。

代码示例: Java代码 收藏代码

  1. CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");
  2. manager.addCache("testCache");
  3. Cache test = singletonManager.getCache("testCache");
  4. test.put(new Element("key1", "value1"));
  5. manager.shutdown();
    CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml"); manager.addCache("testCache"); Cache test = singletonManager.getCache("testCache"); test.put(new Element("key1", "value1")); manager.shutdown();

当然,也支持这种类似DSL的配置方式,配置都是可以在运行时动态修改的:

Java代码 收藏代码

  1. Cache testCache = new Cache(
  2. new CacheConfiguration("testCache", maxElements)
  3. .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU)
  4. .overflowToDisk(true)
  5. .eternal(false)
  6. .timeToLiveSeconds(60)
  7. .timeToIdleSeconds(30)
  8. .diskPersistent(false)
  9. .diskExpiryThreadIntervalSeconds(0));
    Cache testCache = new Cache( new CacheConfiguration("testCache", maxElements) .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) .overflowToDisk(true) .eternal(false) .timeToLiveSeconds(60) .timeToIdleSeconds(30) .diskPersistent(false) .diskExpiryThreadIntervalSeconds(0));

事务的例子:

Java代码 收藏代码

  1. Ehcache cache = cacheManager.getEhcache("xaCache");
  2. transactionManager.begin();
  3. try {
  4. Element e = cache.get(key);
  5. Object result = complexService.doStuff(element.getValue());
  6. cache.put(new Element(key, result));
  7. complexService.doMoreStuff(result);
  8. transactionManager.commit();
  9. } catch (Exception e) {
  10. transactionManager.rollback();
  11. }
    Ehcache cache = cacheManager.getEhcache("xaCache"); transactionManager.begin(); try { Element e = cache.get(key); Object result = complexService.doStuff(element.getValue()); cache.put(new Element(key, result)); complexService.doMoreStuff(result); transactionManager.commit(); } catch (Exception e) { transactionManager.rollback(); }

四、一致性模型

说到一致性,数据库的一致性是怎样的?不妨先来回顾一下数据库的几个隔离级别:

未提交读(Read Uncommitted):在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的数据。会出现脏读、不可重复读、幻象读。 已提交读(Read Committed):只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。已提交读是数据库的默认隔离级别。会出现不可重复读、幻象读。 可重复读(Repeatable Read):像已提交读级别那样读数据,但会保持共享锁直到事务结束。会出现幻象读。 可序列化(Serializable):工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围,这就阻止了新数据插入查询所涉及的范围。

基于以上,再来对比思考下面的一致性模型:

1、强一致性模型:系统中的某个数据被成功更新(事务成功返回)后,后续任何对该数据的读取操作都得到更新后的值。这是传统关系数据库提供的一致性模型,也是关系数据库深受人们喜爱的原因之一。强一致性模型下的性能消耗通常是最大的。

2、弱一致性模型:系统中的某个数据被更新后,后续对该数据的读取操作得到的不一定是更新后的值,这种情况下通常有个“不一致性时间窗口”存在:即数据更新完成后在经过这个时间窗口,后续读取操作就能够得到更新后的值。

3、最终一致性模型:属于弱一致性的一种,即某个数据被更新后,如果该数据后续没有被再次更新,那么最终所有的读取操作都会返回更新后的值。

最终一致性模型包含如下几个必要属性,都比较好理解:

  • 读写一致:某线程A,更新某条数据以后,后续的访问全部都能取得更新后的数据。
  • 会话内一致:它本质上和上面那一条是一致的,某用户更改了数据,只要会话还存在,后续他取得的所有数据都必须是更改后的数据。
  • 单调读一致:如果一个进程可以看到当前的值,那么后续的访问不能返回之前的值。
  • 单调写一致:对同一进程内的写行为必须是保序的,否则,写完毕的结果就是不可预期的了。

4、Bulk Load:这种模型是基于批量加载数据到缓存里面的场景而优化的,没有引入锁和常规的淘汰算法这些降低性能的东西,它和最终一致性模型很像,但是有批量、高速写和弱一致性保证的机制。

这样几个API也会影响到一致性的结果:

1、显式锁(Explicit Locking ):如果我们本身就配置为强一致性,那么自然所有的缓存操作都具备事务性质。而如果我们配置成最终一致性时,再在外部使用显式锁API,也可以达到事务的效果。当然这样的锁可以控制得更细粒度,但是依然可能存在竞争和线程阻塞。

2、无锁可读取视图(UnlockedReadsView):一个允许脏读的decorator,它只能用在强一致性的配置下,它通过申请一个特殊的写锁来比完全的强一致性配置提升性能。

举例如下,xml配置为强一致性模型: Xml代码 收藏代码

  1. <cache name="myCache"
  2. maxElementsInMemory="500"
  3. eternal="false"
  4. overflowToDisk="false"

但是使用UnlockedReadsView:

Java代码 收藏代码

  1. Cache cache = cacheManager.getEhcache("myCache");
  2. UnlockedReadsView unlockedReadsView = new UnlockedReadsView(cache, "myUnlockedCache");
    Cache cache = cacheManager.getEhcache("myCache"); UnlockedReadsView unlockedReadsView = new UnlockedReadsView(cache, "myUnlockedCache");

3、原子方法(Atomic methods):方法执行是原子化的,即CAS操作(Compare and Swap)。CAS最终也实现了强一致性的效果,但不同的是,它是采用乐观锁而不是悲观锁来实现的。在乐观锁机制下,更新的操作可能不成功,因为在这过程中可能会有其他线程对同一条数据进行变更,那么在失败后需要重新执行更新操作。现代的CPU都支持CAS原语了。 Java代码 收藏代码

  1. cache.putIfAbsent(Element element);
  2. cache.replace(Element oldOne, Element newOne);
  3. cache.remove(Element);
    cache.putIfAbsent(Element element); cache.replace(Element oldOne, Element newOne); cache.remove(Element);

五、缓存拓扑类型

1、独立缓存(Standalone Ehcache):这样的缓存应用节点都是独立的,互相不通信。

2、分布式缓存(Distributed Ehcache):数据存储在Terracotta的服务器阵列(Terracotta Server Array,TSA)中,但是最近使用的数据,可以存储在各个应用节点中。

逻辑视角:

L1缓存就在各个应用节点上,而L2缓存则放在Cache Server阵列中。

组网视角:

模型存储视角:

L1级缓存是没有持久化存储的。另外,从缓存数据量上看,server端远大于应用节点。

3、复制式缓存(Replicated Ehcache):缓存数据时同时存放在多个应用节点的,数据复制和失效的事件以同步或者异步的形式在各个集群节点间传播。上述事件到来时,会阻塞写线程的操作。在这种模式下,只有弱一致性模型。

它有如下几种事件传播机制:RMI、JGroups、JMS和Cache Server。

RMI模式下,所有节点全部对等:

JGroup模式:可以配置单播或者多播,协议栈和配置都非常灵活。

Xml代码 收藏代码

  1. <cacheManagerPeerProviderFactory
  2. class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
  3. properties="connect=UDP(mcast_addr=231.12.21.132;mcast_port=45566;):PING:
  4. MERGE2:FD_SOCK:VERIFY_SUSPECT:pbcast.NAKACK:UNICAST:pbcast.STABLE:FRAG:pbcast.GMS"
  5. propertySeparator="::"
  6. />

JMS模式:这种模式的核心就是一个消息队列,每个应用节点都订阅预先定义好的主题,同时,节点有元素更新时,也会发布更新元素到主题中去。JMS规范实现者上,Open MQ和Active MQ这两个,Ehcache的兼容性都已经测试过。

Cache Server模式:这种模式下存在主从节点,通信可以通过RESTful的API或者SOAP。

无论上面哪个模式,更新事件又可以分为updateViaCopy或updateViaInvalidate,后者只是发送一个过期消息,效率要高得多。

复制式缓存容易出现数据不一致的问题,如果这成为一个问题,可以考虑使用数据同步分发的机制。

即便不采用分布式缓存和复制式缓存,依然会出现一些不好的行为,比如:

缓存漂移(Cache Drift):每个应用节点只管理自己的缓存,在更新某个节点的时候,不会影响到其他的节点,这样数据之间可能就不同步了。这在web会话数据缓存中情况尤甚。

数据库瓶颈(Database Bottlenecks ):对于单实例的应用来说,缓存可以保护数据库的读风暴;但是,在集群的环境下,每一个应用节点都要定期保持数据最新,节点越多,要维持这样的情况对数据库的开销也越大。

六、存储方式

1、堆内存储:速度快,但是容量有限。

2、堆外(OffHeapStore)存储:被称为BigMemory,只在企业版本的Ehcache中提供,原理是利用nio的DirectByteBuffers实现,比存储到磁盘上快,而且完全不受GC的影响,可以保证响应时间的稳定性;但是direct buffer的在分配上的开销要比heap buffer大,而且要求必须以字节数组方式存储,因此对象必须在存储过程中进行序列化,读取则进行反序列化操作,它的速度大约比堆内存储慢一个数量级。

(注:direct buffer不受GC影响,但是direct buffer归属的的JAVA对象是在堆上且能够被GC回收的,一旦它被回收,JVM将释放direct buffer的堆外空间。)

3、磁盘存储。

七、缓存使用模式

cache-aside:直接操作。先询问cache某条缓存数据是否存在,存在的话直接从cache中返回数据,绕过SOR;如果不存在,从SOR中取得数据,然后再放入cache中。

Java代码 收藏代码

  1. public V readSomeData(K key)
  2. {
  3. Element element;
  4. if ((element = cache.get(key)) != null) {
  5. return element.getValue();
  6. }
  7. if (value = readDataFromDataStore(key)) != null) {
  8. cache.put(new Element(key, value));
  9. }
  10. return value;
  11. }
    public V readSomeData(K key) { Element element; if ((element = cache.get(key)) != null) { return element.getValue(); } if (value = readDataFromDataStore(key)) != null) { cache.put(new Element(key, value)); } return value; }

cache-as-sor:结合了read-through、write-through或write-behind操作,通过给SOR增加了一层代理,对外部应用访问来说,它不用区别数据是从缓存中还是从SOR中取得的。

read-through。

write-through。

write-behind(write-back):既将写的过程变为异步的,又进一步延迟写入数据的过程。

Copy Cache的两个模式:CopyOnRead和CopyOnWrite。

CopyOnRead指的是在读缓存数据的请求到达时,如果发现数据已经过期,需要重新从源处获取,发起的copy element的操作(pull);

CopyOnWrite则是发生在真实数据写入缓存时,发起的更新其他节点的copy element的操作(push)。

前者适合在不允许多个线程访问同一个element的时候使用,后者则允许你自由控制缓存更新通知的时机。

更多push和pull的变化和不同,也可参见这里

八、多种配置方式

包括配置文件、声明式配置、编程式配置,甚至通过指定构造器的参数来完成配置,配置设计的原则包括:

所有配置要放到一起

缓存的配置可以很容易在开发阶段、运行时修改

错误的配置能够在程序启动时发现,在运行时修改出错则需要抛出运行时异常

提供默认配置,几乎所有的配置都是可选的,都有默认值

九、自动资源控制(Automatic Resource Control,ARC):

它是提供了一种智能途径来控制缓存,调优性能。特性包括:

内存内缓存对象大小的控制,避免OOM出现

池化(cache manager级别)的缓存大小获取,避免单独计算缓存大小的消耗

灵活的独立基于层的大小计算能力,下图中可以看到,不同层的大小都是可以单独控制的

可以统计字节大小、缓存条目数和百分比

优化高命中数据的获取,以提升性能,参见下面对缓存数据在不同层之间的流转的介绍

缓存数据的流转包括了这样几种行为:

Flush:缓存条目向低层次移动。

Fault:从低层拷贝一个对象到高层。在获取缓存的过程中,某一层发现自己的该缓存条目已经失效,就触发了Fault行为。

Eviction:把缓存条目除去。

Expiration:失效状态。

Pinning:强制缓存条目保持在某一层。

下面的图反映了数据在各个层之间的流转,也反映了数据的生命周期:

十、监控功能

监控的拓扑:

每个应用节点部署一个监控探针,通过TCP协议与监控服务器联系,最终将数据提供给富文本客户端或者监控操作服务器。

十一、广域网复制

缓存数据复制方面,Ehcache允许两个地理位置各异的节点在广域网下维持数据一致性,同时它提供了这样几种方案(注:下面的示例都只绘制了两个节点的情形,实际可以推广到N个节点):

第一种方案:Terracotta Active/Mirror Replication。

这种方案下,服务端包含一个活跃节点,一个备份节点;各个应用节点全部靠该活跃节点提供读写服务。这种方式最简单,管理容易;但是,需要寄希望于理想的网络状况,服务器之间和客户端到服务器之间都存在走WAN的情况,这样的方案其实最不稳定。

第二种方案:Transactional Cache Manager Replication。

这种方案下,数据读取不需要经过WAN,写入数据时写入两份,分别由两个cache manager处理,一份在本地Server,一份到其他Server去。这种方案下读的吞吐量较高而且延迟较低;但是需要引入一个XA事务管理器,两个cache manager写两份数据导致写开销较大,而且过WAN的写延迟依然可能导致系统响应的瓶颈。

第三种方案:Messaging based (AMQ) replication。

这种方案下,引入了批量处理和队列,用以减缓WAN的瓶颈出现,同时,把处理读请求和复制逻辑从Server Array物理上就剥离开,避免了WAN情况恶化对节点读取业务的影响。这种方案要较高的吞吐量和较低的延迟,读/复制的分离保证了可以提供完备的消息分发保证、冲突处理等特性;但是它较为复杂,而且还需要一个消息总线。

有一些Ehcache特性应用较少或者比较边缘化,没有提到,例如对于JMX的支持;还有一些则是有类似的特性和介绍了,例如对于WEB的支持,请参见我这篇关于OSCache的解读,其中的“web支持”一节有详细的原理分析。

最后,关于Ehcache的性能比对,下面这张图来自Ehcache的创始人Greg Luck的blog

put/get上Ehcache要500-1000倍快过Memcached。原因何在?他自己分析道:“In-process caching and asynchronous replication are a clear performance winner”。有关它详细的内容还是请参阅他的blog吧。

文章系本人原创,转载请注明出处和作者

  • 大小: 45.2 KB

  • 大小: 64 KB

  • 大小: 42.4 KB

  • 大小: 46.1 KB

  • 大小: 22.4 KB

  • 大小: 65.2 KB

  • 大小: 85.1 KB

  • 大小: 28.4 KB

  • 大小: 34.7 KB

  • 大小: 52.6 KB

  • 大小: 90.1 KB

  • 大小: 94.3 KB

  • 大小: 132.2 KB

  • 大小: 20.4 KB

  • 查看图片附件

5

2

分享到: 设计一套缓存框架需要关注的要素 | “你不适合做程序员”

1 楼 mowengaobo 2012-07-10

写得真不错,不过对 Greg Luck最后那张图表示怀疑。

发表评论

您还没有登录,请您登录后再发表评论

RayChase的博客

RayChase

  • 浏览: 113254 次
  • 性别: Icon_minigender_1
  • 来自: 北京
  • 最近访客 更多访客>>

fudewei1的博客

fudewei1

hi00o的博客

hi00o macrochen的博客

macrochen

dylinshi126的博客

dylinshi126

博客专栏

1489d3fc-d068-34f7-9f25-23a663e958b1 J2EE 核心模式 浏览量:16253 C976ce7b-2227-36df-bb1a-47f0139cc7ac JavaScript重构 浏览量:12707

文章分类

社区版块

评论排行榜

Using CAS without the Login Screen

Posted on

Using CAS without the Login Screen - Central Authentication Service - Jasig Wiki

  1. Dashboard")
  2. Central Authentication Service
  3. Home
  4. Using CAS without the Login Screen

Using CAS without the Login Screen

Skip to end of metadata

Motivation

We wanted to be more flexible in the use of the login UI, so e.g. wanted to embed it in several places as a small panel. Moreover, we wanted to understand CAS as a pure service, not having to maintain layout information twice. Also, we wanted to support both, login postings from another site and direct login at the CAS server as a fallback.

Proposed Solutions that do not work

There have been two proposals, which we haven't found to be suitable:

  • AJAX: Ajax requests are sandboxed and that sandbox is even more strict than the two-dot rule applied to cookies: It really has to be exactly the same server name in order to work
  • IFrames: In this posting to the cas-dev mailing list, it has been suggested to use an iFrame to embed a login panel on a remote site. This has two limitations:

  • How to get rid of the Frame after login? in the default view a successful login would lead to redirect only in that frame. If you use target="parent" in the from tag, how do you display errors then?

  • You would have limited layout possibilities.

    So for our requirements, none of the proposed solutions were adequate.

Our Solution

We decided to use Javascript triggered redirects, which allows us to use this mechanism also on static html pages (e.g. CMS based contents). While loading we request a login ticket, then the login form displays and then it submits to the CAS login URL. If Errors are present at CAS we redirect to the referring page, adding a parameter "error_message" to that request.

Client side

Here is the static remote login form we use: ?<!DOCTYPE html PUBLIC

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd"

>

Test remote Login using JS

Test remote Login using JS

Benutzer:
Password:
Login Ticket:
Service:

CAS side

Adapting only the views and introducing if-else statements there meant having logic that belongs to the controllers in the view. Therefore we decided to inject our add-on into the spring web flow. Here are the changes we have made to login-webflow.xml (changes highlighted in blue): ?<?xml version=

"1.0"

encoding=

"UTF-8"

?>

? ?

Problems encountered and suggested solutions

The solution works well, but it has a very dirty edge: introducing a new event in the submit method that allows us to distinguish between a login posting from the CAS Site and another site. Here we had to redeclare the submit method in org.jasig.cas.web.flow.AuthenticationViaFormAction (changes marked in blue): ?[...]

try

{

final

String ticketGrantingTicketId =

this

.centralAuthenticationService

.createTicketGrantingTicket(credentials);

ContextUtils.addAttribute(context,

AbstractLoginAction.REQUEST_ATTRIBUTE_TICKET_GRANTING_TICKET,

ticketGrantingTicketId);

setWarningCookie(response, warn);

return

success();

}

catch

(

final

TicketException e) {

populateErrorsInstance(context, e);

        // START: ChangesBusinessMart: check, whether the posting has been sent from a remote server
        String myServerName = request.getLocalName();
        String referrer = request.getParameter("login-at");
        if (referrer != null && referrer.indexOf(myServerName) == -1) {
            return result("errorForRemoteRequestor");
        }

?return

error();

} [...]

This could have been avoided by

  1. making this submit method overridable (non-final) to add new events in a derived class. Or
  2. to make such a distinction of events in the standard CAS distribution.

Maybe this can be accomplished somehow in future versions of CAS?

Listing of the other files mentioned in this solution

provideLoginTicketToRemoteRequestorAction -> introduces a new parameter get-lt to signal that a new login ticket shall be issued to the HTTP Referer: ?package

de.businessmart.sso.cas.flow;

import

javax.servlet.http.HttpServletRequest;

import

javax.servlet.http.HttpServletResponse;

import

org.jasig.cas.web.flow.util.ContextUtils; import

org.springframework.beans.factory.InitializingBean;

import

org.springframework.webflow.action.AbstractAction; import

org.springframework.webflow.core.collection.MutableAttributeMap;

import

org.springframework.webflow.execution.Event; import

org.springframework.webflow.execution.RequestContext;

import

de.businessmart.sso.cas.CasUtility;

///

/* Opens up the CAS web flow to allow external retrieval of a login ticket.

/* @author konrad.wulf

/*

/*/

public

class

ProvideLoginTicketToRemoteRequestorAction

extends

AbstractAction {

@Override

protected

Event doExecute(RequestContext context)

throws

Exception

{

final

HttpServletRequest request = ContextUtils.getHttpServletRequest(context);

if

(request.getParameter(

"get-lt"

) !=

null

&& request.getParameter(

"get-lt"

).equalsIgnoreCase(

"true"

)) {

return

result(

"loginTicketRequested"

);

}

return

result(

"continue"

);

}

}

viewRedirectToRequestor actually does the redirects to the referrer:

?<%

@page

import

=

"de.businessmart.sso.cas.CasUtility"

%>

<%@ taglib prefix=

"c"

uri=

"http://java.sun.com/jsp/jstl/core"

%> <%@ taglib prefix=

"spring"

uri=

"http://www.springframework.org/tags"

%>

<% String separator =

""

; String referrer = request.getHeader(

"Referer"

);

referrer = CasUtility.resetUrl(referrer); if

(referrer !=

null

&& referrer.length() >

0

) {

separator =(referrer.indexOf(

"?"

) > -

1

)?

"&"

:

"?"

; %>

<% }

else {

out.print(

"You better know what to do here."

); } %>

And for completeness, here is the CasUtility that removes obsolete parameters from the query string: ?package

de.businessmart.sso.cas;

public

class

CasUtility {

///

/* Removes the previously attached GET parameters "lt" and "error_message" to be able to send new ones.

/* @param casUrl

/* @return

/*/

public

static

String resetUrl ( String casUrl) {

String cleanedUrl;

String[] paramsToBeRemoved =

new

String[]{

"lt"

,

"error_message"

,

"get-lt"

};

cleanedUrl = removeHttpGetParameters(casUrl, paramsToBeRemoved);

return

cleanedUrl;

}

///

/* Removes selected HTTP GET parameters from a given URL

/* @param casUrl

/* @param paramsToBeRemoved

/* @return

/*/

public

static

String removeHttpGetParameters (String casUrl, String[] paramsToBeRemoved) {

String cleanedUrl = casUrl;

if

(casUrl !=

null

)

{

// check if there is any query string at all

if

(casUrl.indexOf(

"?"

) == -

1

)

{

return

casUrl;

}

else

{

// determine the start and end position of the parameters to be removed

int

startPosition, endPosition;

boolean

containsOneOfTheUnwantedParams =

false

;

for

(String paramToBeErased : paramsToBeRemoved)

{

startPosition = -

1

;

endPosition = -

1

;

if

(cleanedUrl.indexOf(

"?"

  • paramToBeErased +

"="

) > -

1

)

{

startPosition = cleanedUrl.indexOf(

"?"

  • paramToBeErased +

"="

) +

1

;

}

else

if

(cleanedUrl.indexOf(

"&"

  • paramToBeErased +

"="

) > -

1

)

{

startPosition = cleanedUrl.indexOf(

"&"

  • paramToBeErased +

"="

) +

1

;

}

if

(startPosition > -

1

)

{

int

temp = cleanedUrl.indexOf(

"&"

, startPosition);

endPosition = (temp > -

1

) ? temp +

1

: cleanedUrl

.length();

// remove that parameter, leaving the rest untouched

cleanedUrl = cleanedUrl.substring(

0

, startPosition)

  • cleanedUrl.substring(endPosition);

containsOneOfTheUnwantedParams =

true

;

}

}

// wenn nur noch das Fragezeichen vom query string übrig oder am schluss ein "&", dann auch dieses entfernen

if

(cleanedUrl.endsWith(

"?"

) || cleanedUrl.endsWith(

"&"

))

{

cleanedUrl = cleanedUrl.substring(

0

,

cleanedUrl.length() -

1

);

}

// erst zurückgeben, wenn wir auch überprüft haben, ob nicht ein parameter mehrfach angegeben wurde...

if

(!containsOneOfTheUnwantedParams)

return

casUrl;

else

cleanedUrl = removeHttpGetParameters(cleanedUrl, paramsToBeRemoved);

}

}

return

cleanedUrl;

}

} Labels:

None

Edit Labels")

8 Comments

Hide/Show Comments

  1. User icon: battags

Apr 25, 2007

Scott Battaglia

The IFRAME method actually does work. As stated in emails on the CAS developer's list, it requires replacing the default view with a JavaScript redirect view.

This allows the following: (a) errors will continue to be displayed correctly (in the iframe) (b) redirects the browser correctly (c) usernames and passwords are never collected on the client application

Jul 09, 2007

Alberto Mozzone

Hi there, I made the changes described here, but I'm stuck with these problems:

  • Where the file "viewRedirectToRequestor.jsp" must be placed ?
  • There isn't the definition of bean with class "ProvideLoginTicketToRemoteRequestorAction" in "applicationContext.xml".
  • After adding it, I get the exception:

2007-07-09 17:08:18,773 ERROR [org.apache.catalina.core.ContainerBase.[Catalina].[auth.domain.com].[/].[cas]] - Servlet.service() for servlet cas threw exception java.lang.InstantiationException at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:30)

And CAS shows its generic exception page "CAS is unavailable".

Where am I wrong ?

Somebody can help, please ?

Thanks in advance.

Alberto

Dec 07, 2007

Kevin McKee

A much simpler and elegant version is to not deal with getting a login ticket at all and to simply auto submit the CAS form via Javascript if a param such as 'auto' is submitted to the login form along with the service, username, and password parameters.

Example of casLoginView.jsp: ? <%

// If we recieve the 'auto' parameter as 'true' we attempt to // auto populate the form from the request variables and submit it

String auto = request.getParameter(

"auto"

); if

(auto !=

null

&& auto.equals(

"true"

)) {

%>

<% }

%>

No fuss, no muss. CAS will then redirect back to your service url with a ticket that is ready to be validated. This comes in really handy if you are doing something like creating users in your system and then auto logging them in after the creation process.

I know purists will hate the fact that we put a scriptlet in there (i'm not too wild about it either), but it is alot easier than all the redirects involved for grabbing a login ticket just to submit credentials.

Just a note, you will also want to change the name of the submit button from 'submit' or else Javascript will have a fit. Hope this helps.

Jun 06, 2008

Konrad Wulf

Here's a tardy response to Alberto Mozzone's post. Sorry for not having answered any earlier. But better late than never (wink) It still seems to be of interest:

I have forgotten to mention in this article that the view bmRedirectToRequestorView needs to be defined in the default_views.properties file, which is located at "/WEB-INF/classes". The following entries need to be added there:

/#/#/# Redirect with login ticket view bmRedirectToRequestorView.(class)=org.springframework.web.servlet.view.JstlView bmRedirectToRequestorView.url=/WEB-INF/view/jsp/default/ui/bmRedirectToRequestorView.jsp

Moreover, Alberto was also quite right that the bean "provideLoginTicketToRemoteRequestorAction" has to be defined somewhere. I did that in /WEB-INF/cas-servlet.xml:

id="provideLoginTicketToRemoteRequestorAction" class="de.businessmart.sso.cas.flow.ProvideLoginTicketToRemoteRequestorAction" />

With these 2 little additions the solution description should be complete.

Oct 13, 2010

gaurav saxena

Hi,

This was a nice article. But i need to do the same with that latest version of CAS i.e. 3.4 . Can you please provide me the steps for the same. Atleast the stucture for login-webflow.xml as it is now using spring-webflow-2.0.xsd .

Thanks in advance.

Gaurav Saxena

  1. User icon: eholderman

Feb 02, 2011

Ed Holderman

I have the same question. Does anyone have any tips on doing this in CAS 3.4.x and is there now a different way of accomplishing it with the new version? My end goal is to alter the Liferay login portlet to submit credentials to CAS over https. Out of the box IFrame portlets would hard code the service URL, breaking deep link capabilities. Thank you - Ed

Jun 13, 2011

Robert DeMuth

Looking to do the same thing in CAS 3.4.6 (using the Maven2 overlay method). It looks like login-webflow.xml has changed dramatically since this page was created.

  1. User icon: patino@fordham.edu

Oct 17, 2011

jpats

Hi, I just want to find out if you were able to have this working. If you did, really would like to find out how you did it. Thanks in advance.