﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-陈于喆的技术博客-随笔分类-缓存</title><link>http://www.blogjava.net/dongbule/category/46031.html</link><description>构建高性能门户网</description><language>zh-cn</language><lastBuildDate>Wed, 08 Dec 2010 18:23:57 GMT</lastBuildDate><pubDate>Wed, 08 Dec 2010 18:23:57 GMT</pubDate><ttl>60</ttl><item><title>memcached一些应用点滴</title><link>http://www.blogjava.net/dongbule/archive/2010/12/08/340066.html</link><dc:creator>陈于喆</dc:creator><author>陈于喆</author><pubDate>Wed, 08 Dec 2010 07:00:00 GMT</pubDate><guid>http://www.blogjava.net/dongbule/archive/2010/12/08/340066.html</guid><wfw:comment>http://www.blogjava.net/dongbule/comments/340066.html</wfw:comment><comments>http://www.blogjava.net/dongbule/archive/2010/12/08/340066.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/dongbule/comments/commentRss/340066.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/dongbule/services/trackbacks/340066.html</trackback:ping><description><![CDATA[大名鼎鼎的分布式缓存系统memcached，在开源社区中可谓是无人不知无人不晓，memcached支持分布式的横向扩展，但memcached的服务端却是单实例，并无"分布式"的功能，所谓的分布式只是客户端在存储的主键做分布的存储；还有memcached组件缓存对象，如果组件无进行序列化必定无法正确取得数据；如何使用memcached的java组件来监控memcached的运行状态；以上等等的问题是我在日常的工作中碰到并解决的,拿出来跟大家做个分享^_^<br />
<br />
<hr width="100%" size="2" />
<br />
<strong>对象的序列化</strong><br />
首先memcached是独立的服务器组件,独立于应用系统,从客户端保存和读取对象到memcached是必须通过网络传输,因为网络传输都是二进制的数据,所以所有的对象都必须经过序列化,否则无法存储到memcahced的服务器端.<br />
正如我们以往在集群中应用的序列化一样,memcached的序列化的性能也是往往让大家头疼,如果我们对我们的domain类进行对象的序列化,第一次序列化时间会比较长,但后续会优化,也就是说序列化最大的消耗不是对象的序列化,而是类的序列化,如果存储的只是一个String对象,这种情况是最理想的,省去了序列化的操作.实际上String对象本身已经实现了序列化接口,无法我们再次去进行序列化操作.<br />
<br />
<hr width="100%" size="2" />
<br />
<strong>memcached的原子加法</strong><br />
记录一下上次犯得一个错误<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&lt;%</span><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;count&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
count</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
MemCachedClient&nbsp;mcc&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MemCachedClient();<br />
mcc.add(</span><span style="color: #000000;">"</span><span style="color: #000000;">test.html</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;count);<br />
</span><span style="color: #000000;">%&gt;</span></div>
<br />
这段代码的作用是将test.html的用户访问次数保存到memcached中,粗劣一看好像并无错误,但在高并发时的出来的访问数据一定是小于实际的访问数量,当然这里并不是memcached对象锁的问题,主要还是程序中线程的同步问题,但是如果使用java的synchronized或lock那么在性能上肯定是无法忍受的,memcached客户端组件带有原子性的加法和减法<br />
<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&lt;%</span><span style="color: #000000;"><br />
MemCachedClient&nbsp;mcc&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;MemCachedClient();<br />
System.out.println(mcc.addOrIncr(</span><span style="color: #000000;">"</span><span style="color: #000000;">test.html</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">1</span><span style="color: #000000;">));<br />
</span><span style="color: #000000;">%&gt;</span></div>
<br />
long addORIncr(String key,long inc)为计数器值增加inc,如果计数器不存在,则保存inc为计数器的值,必须注意的是服务器端不会对超过2的32次方的行为进行检查<br />
<br />
<hr width="100%" size="2" />
<br />
<br />
<strong>分布式的mencached</strong><br />
memcached虽然是属于分布式的缓存服务器,但实际上memcached服务端之间并无分布式的功能,不会互相通信共享数据,如何进行分布式,这完全是取决于客户端的实现<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/memcached/2.png" width="456" height="390" /><br />
<br />
假设我们现在有三台memcached服务器分别为node1，node2，node3，应用程序要保存键名分别为"test1","test2","test3",客户端实现的算法就是根据键名来决定保存数据的memcached服务器,我们将"test1"保存到node1，"test2"保存到node2，"test3"保存到node3，并且在读取缓存数据也是通过一样的算法从各台服务器上读取相应的key,这样通过一个最简单的算法将不同的键保存到不同的服务器上,实现了memcached的分布式.<br />
但是这种算法很难确保每台服务器得到较为平均的数据量,我们需要改变一下客户端的算法,简单来说,就是根据服务器的台数的余数进行分散<br />
<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&lt;%<br />
"</span><span style="color: #000000;">test1</span><span style="color: #000000;">"</span><span style="color: #000000;">.hashCode()</span><span style="color: #000000;">%</span><span style="color: #000000;">3<br />
%&gt;<br />
</span></div>
<br />
根据key的java.lang.String.hashCode()取得散列值,再将值模服务器的台数得到余数值,我们再根据这个余数值来判定这个key要存入哪一台服务器,当key的数量越来越大,对key的散列取模也会趋向平均,基本可以保证几台memcached服务器所存储的缓存量趋向平均<br />
似乎很完美,余数计算的方法很简单,数据的分散性也很优秀,但也有其缺点,就是当需要添加或移除服务器时,缓存的重组代价是相当巨大的,添加或移除服务器时,余数就会发生变化,这样就无法取到与原来缓存时相同的服务器.<br />
网上介绍的Consistent Hashing算法基本上可以解决这个问题,这里做个简单的说明,首先是求出memcached服务器节点的哈希值,并将其配置到0-2的32次方的圆上,然后用同样的方法求出存储数据的键的哈希值,并映射到圆上.然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上.如果超过2的32次方仍然找不到服务器,就会保存到第一台memcached服务器上<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/memcached/3.png" width="512" height="421" /><br />
<br />
从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率，但Consistent Hashing中，只有在continuum上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响<br />
<br />
<hr width="100%" size="2" />
<br />
<strong>几种连接客户端的对比</strong><br />
目前java的memcached主要有Java-Memcached-Client,Xmemached,Spymemcached三种,这三个客户端的性能测试可以看<br />
http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/memcached/4.png" width="942" height="612" /><br />
请求的资源为64Bytes,在低并发Java-Memcached-Client是占有一定的优势,但在并发数超过100以后,Java-Memcached-Client是呈现直线下跌,并发数达到300已经无法承受,Spymemcached和Xmemached表现相对稳定,特别是Xmemached无论在低并发或高并发都保持优秀的性能表现<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/memcached/5.png" width="950" height="610" /><br />
<br />
并发数固定为100时,在小文件的请求Java-Memcached-Client还是占有优势,当随着请求的size越来越大,三者趋向于同一点<br />
如果你对memcached访问的负载不高,那么Java-Memcached-Client是一个不错的选择,如果你对memcached访问的负载要求较高,推荐使用Xmemached,如果需要异步的批量处理,可以选择Spymemcached,如果你什么都不知道,那么建议使用Xmemached,因为无论在何种情况,它都可以表现出较好的性能,虽然不是最好<br />
<br />
<hr width="100%" size="2" />
<br />
<strong>监控memcached </strong><br />
推荐使用nagios或cactis进行监控,nagios没有配置过,cactis是需要下载一个脚本插件<br />
这里推荐一个从网上淘来的php,只要把它放到你的机器中,当然你的机器要支持php环境,将此php放入你的网页访问网络就可以访问<br />
下载<br />
<a href="/Files/dongbule/cacti/memcache.rar">http://www.blogjava.net/Files/dongbule/cacti/memcache.rar</a><br />
修改php以下几个选项<br />
<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008080;">define</span><span style="color: #000000;">(</span><span style="color: #000000;">'</span><span style="color: #000000;">ADMIN_USERNAME</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">memcache</span><span style="color: #000000;">'</span><span style="color: #000000;">);&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Admin&nbsp;Username</span><span style="color: #008000;"><br />
</span><span style="color: #008080;">define</span><span style="color: #000000;">(</span><span style="color: #000000;">'</span><span style="color: #000000;">ADMIN_PASSWORD</span><span style="color: #000000;">'</span><span style="color: #000000;">,</span><span style="color: #000000;">'</span><span style="color: #000000;">password</span><span style="color: #000000;">'</span><span style="color: #000000;">);&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Admin&nbsp;Password</span><span style="color: #008000;"><br />
</span><span style="color: #000000;"><br />
</span><span style="color: #800080;">$MEMCACHE_SERVERS</span><span style="color: #000000;">[]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">192.168.1.100:11211</span><span style="color: #000000;">'</span><span style="color: #000000;">;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;add&nbsp;more&nbsp;as&nbsp;an&nbsp;array</span><span style="color: #008000;"><br />
#</span><span style="color: #008000;">$MEMCACHE_SERVERS[]&nbsp;=&nbsp;'mymemcache-server2:11211';&nbsp;//&nbsp;add&nbsp;more&nbsp;as&nbsp;an&nbsp;array</span></div>
<br />
监控的平台<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/memcached/6.png" width="992" height="422" /><br />
<br />
<hr width="100%" size="2" />
<br />
<strong>理解memcached的删除机制</strong><br />
memcached内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期, 这种技术被称为lazy(惰性)expiration.因此,memcached不会在过期监视上耗费CPU时间<br />
memcached会优先使用已超时的记录的空间,并使用LRU算法来分配空间,因此当memcached的内存空间不足,就从最近违背使用的记录中搜索,并将空间分配给新的记录<br />
不过在某些情况下LRU机制会造成某些麻烦,如你并不想要淘汰已被缓存过的记录,可以在memcached启动时添加 -M 参数来禁止LRU,但这样在memcached的内存用尽时,memcached会返回错误,是否使用LRU,在于你的需求<br />
<br />
<br />
----------------------------------------<br />
<br />
by 陈于喆 <br />
QQ:34174409<br />
Mail: dongbule@163.com<br />
<br />
<br />
<br />
<img src ="http://www.blogjava.net/dongbule/aggbug/340066.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/dongbule/" target="_blank">陈于喆</a> 2010-12-08 15:00 <a href="http://www.blogjava.net/dongbule/archive/2010/12/08/340066.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浏览器的缓存协商</title><link>http://www.blogjava.net/dongbule/archive/2010/08/25/329840.html</link><dc:creator>陈于喆</dc:creator><author>陈于喆</author><pubDate>Tue, 24 Aug 2010 16:03:00 GMT</pubDate><guid>http://www.blogjava.net/dongbule/archive/2010/08/25/329840.html</guid><wfw:comment>http://www.blogjava.net/dongbule/comments/329840.html</wfw:comment><comments>http://www.blogjava.net/dongbule/archive/2010/08/25/329840.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/dongbule/comments/commentRss/329840.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/dongbule/services/trackbacks/329840.html</trackback:ping><description><![CDATA[记得问过身边的一些开发工程师(非前端)缓存要分几个层次，从哪里做起，答案很多，比如反向代理缓存，DNS缓存，memcached，数据库缓存等等，确实很完整，不过好像漏掉了我们用户跟我们联通的最根本的工具浏览器，确实好似很少有人把用户的浏览器当作是web站点的组成部分来看待<br />
<br />
缓存协商<br />
现在我们需要将用户的浏览器也纳入我们构建网站各个缓存层次中的其中一个重要层次，网站信息和内容在由web服务器生成，而将这些信息和内容作为一段二进制的文件作为本地缓存文件存放在用户的浏览器，是两个独立个体共同完成的任务，所以两者之间需要一种沟通的机制，也就是HTTP的缓存协商<br />
<br />
<strong>Last-Modified和If-Modified-Since协商</strong><br />
Last-Modified和If-Modified-Since分别位于响应头信息和请求头信息中，都是记录请求的页面最后的修改时间<br />
在第一次访问web服务器会返回200状态，并在浏览器的响应头Last-Modified上写上此页面最后修改的时间戳<br />
使用firebug进行查看<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/dongbule/1.jpg" width="949" height="430" /><br />
<br />
<br />
在再次访问时，浏览器会把第一次返回Last-Modified的时间戳记录到If-Modified-Since，并作为请求头信息发送到服务区，web服务器会通过If-Modified-Since上的时间戳来判断用户的页面是否是最新的，如果不是最新的，则返回新的页面并修改响应头Last-Modified时间戳给用户，如果判断是最新的，则返回304状态并告诉浏览器本地的cache页面是最新的，浏览器可以直接加载本地页面，这样可以减少网络上传输的数据，并且也减少服务器的负担。<br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/dongbule/2.jpg" alt="" border="0" /><br />
<br />
<br />
这里需要注意的是，HTTP协议中规定使用的是GMT时间(格林威治)，而我们国家使用的是GMT+8的时间，所以我们从firebug中看到的时间是比我们早了8个小时，不过这没关系，它并不影响到缓存的对比工作<br />
<br />
<strong>Etag协商</strong><br />
如果我们的一个文件存放在多台web服务器上，用户的请求在这些服务器上之间轮询，实现负载均衡，那些这个文件在各台web服务器的最后修改时间很可能是不一样，这样用户每次请求到的web服务器都可能是不同，Last-Modified和If-Modified-Since则无法对应，导致每次都需要重新获取内容，这时候采用直接标记内容的Etag算法，就可以避免上述的问题<br />
Etag是由Web服务器生成的，如Apache为一个静态文件新增响应头为<br />
Etag&nbsp;&nbsp; &nbsp;"e3af4060-5-472bedf076880"<br />
浏览器获得这个Etag后，便会在下次请求该页面时，在HTTP的请求头附上If-None-Match&nbsp;&nbsp; &nbsp;"e3af4060-5-472bedf076880"，与web服务器上的Etag值相比较，如果是相同的话，便返回304状态，不同则重新返回新页面信息<br />
<br />
<img src="http://www.blogjava.net/images/blogjava_net/dongbule/3.jpg" alt="" border="0" /><br />
<br />
当然啦，这里要指出浏览器缓存只针对HTML和HTM进行而动态文件则无法缓存，浏览器并不在乎你的文件是否动态还是静态，浏览器只认准和web服务器通讯的HTTP协商，所以只要动态页面HTTP头信息包含的缓存协商信息，动态内容也是可以一样被浏览器进行缓存的，和静态页面无什么两样，只是你确定你需要这样做，让动态页面被缓存？<br />
以下是对一个jsp进行写头信息<br />
<br />
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">response.addHeader(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Last-Modified</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,DateUtl.format(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.Date())</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&nbsp;GMT</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br />
response.addHeader(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">ETag</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">e3af4060-5-472bedf076880</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);</span></div>
<br />
<br />
同样，这样两个设置会写入浏览器的响应头，这个动态文件跟静态文件一样会被浏览器缓存<br />
<br />
<strong>Expires</strong><br />
Expires标记告诉浏览器该页面何时过期，并且在此过期前不需要再访问web服务器，直接使用本地的缓存文件即可，这样请求响应头都不需要，确实节省了带宽和服务器的开销，但是就算页面在web服务器上更新后在Expires过期前也不会出现在用户面前，究竟是否应该在于我们的实际应用和取舍<br />
下面是Apache中提供的mod_expires模块<br />
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">IfModule&nbsp;</span><span style="color: rgb(255, 0, 0);">mod_expires.c</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />
ExpiresActive&nbsp;On<br />
ExpiresDefault&nbsp;A600<br />
ExpiresByType&nbsp;image/x-icon&nbsp;A2592000<br />
ExpiresByType&nbsp;application/x-javascript&nbsp;A604800<br />
ExpiresByType&nbsp;text/css&nbsp;A604800<br />
ExpiresByType&nbsp;image/gif&nbsp;A2592000<br />
ExpiresByType&nbsp;image/png&nbsp;A2592000<br />
ExpiresByType&nbsp;image/jpeg&nbsp;A2592000<br />
ExpiresByType&nbsp;text/plain&nbsp;A86400<br />
ExpiresByType&nbsp;application/x-shockwave-flash&nbsp;A2592000<br />
ExpiresByType&nbsp;video/x-flv&nbsp;A2592000<br />
ExpiresByType&nbsp;application/pdf&nbsp;A2592000<br />
ExpiresByType&nbsp;text/html&nbsp;A600<br />
</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">IfModule</span><span style="color: rgb(0, 0, 255);">&gt;</span></div>
<br />
或者<br />
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">&lt;</span><span style="color: rgb(128, 0, 0);">IfModule&nbsp;</span><span style="color: rgb(255, 0, 0);">mod_expires.c</span><span style="color: rgb(0, 0, 255);">&gt;</span><span style="color: rgb(0, 0, 0);"><br />
ExpiresActive&nbsp;On<br />
ExpiresDefault&nbsp;"access&nbsp;plus&nbsp;12&nbsp;hours"<br />
ExpiresByType&nbsp;text/html&nbsp;"access&nbsp;plus&nbsp;3&nbsp;days"<br />
ExpiresByType&nbsp;text/plain&nbsp;"access&nbsp;plus&nbsp;3&nbsp;days"<br />
ExpiresByType&nbsp;text/css&nbsp;&nbsp;"access&nbsp;plus&nbsp;7&nbsp;days"<br />
ExpiresByType&nbsp;image/gif&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
ExpiresByType&nbsp;image/png&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
ExpiresByType&nbsp;image/jpeg&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
ExpiresByType&nbsp;image/x-icon&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
ExpiresByType&nbsp;video/x-flv&nbsp;&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
ExpiresByType&nbsp;application/x-shockwave-flash&nbsp;"access&nbsp;plus&nbsp;30&nbsp;days"<br />
</span><span style="color: rgb(0, 0, 255);">&lt;/</span><span style="color: rgb(128, 0, 0);">IfModule</span><span style="color: rgb(0, 0, 255);">&gt;</span></div>
<br />
<br />
我们在jsp文件也可以指定expires的过期时间<br />
<br />
<div style="background-color: rgb(238, 238, 238); font-size: 13px; border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">response.addHeader(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Expires</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">,DateUtl.format(</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.Date())</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">&nbsp;GMT</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);</span></div>
<br />
<img src="http://www.blogjava.net/images/blogjava_net/dongbule/4.jpg" alt="" border="0" /><br />
<br />
当然啦，你再刷新也不会返回304状态，因为浏览器已经不用想web服务器发出请求<br />
<br />
----------------------------------------<br />
<br />
by 陈于喆 <br />
Mail: chenyz@corp.netease.com
<br />
<br />
<br />
参考文章<br />
郭欣 《构建高新能web站点》<br />
中文版 《HTTP协议(RFC2616)》<br />
<img src ="http://www.blogjava.net/dongbule/aggbug/329840.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/dongbule/" target="_blank">陈于喆</a> 2010-08-25 00:03 <a href="http://www.blogjava.net/dongbule/archive/2010/08/25/329840.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>