?
在我們打開瀏覽器
,
決定瀏覽某個(gè)網(wǎng)頁之前
(
指人眼看到屏幕上的內(nèi)容之前
),
一般來說瀏覽器有幾個(gè)事情要做
,
首先根據(jù)
url
請(qǐng)求服務(wù)器端的
html
將
html
顯示到屏幕上等等
.
à
下載
css,
和
js,--------
à
,
然后解析
html,------
à
數(shù)據(jù)
------
接著大腦才能感受到
.
à
然后眼睛才能感受到
,--------
à
---------
在這個(gè)流程中
,
那么怎么才能讓大腦盡可能快的接受到這個(gè)信息呢
,
我想最快的方式是在大腦里放一份該屏幕的拷貝
,
下次想看這份內(nèi)容的時(shí)候直接拿出大
腦的拷貝就可以了
.
如果大腦容量有限
,
那我們可以考慮把這份拷貝放到眼睛里
,
如果眼睛也放不下
,
那我們可以考慮把這份拷貝放到瀏覽器里
,
從這個(gè)邏輯上看
,
越靠近大腦的數(shù)據(jù)越能快速的被我們接受到
.
那么本文的目的其實(shí)就是為了研究如何使用大腦和眼睛來緩存數(shù)據(jù)
------------------------
吃驚吧
,ahuaxuan
瞎扯的
,
回到正題
,
上面這段調(diào)侃不是為了說明別的
,
而是為了說明越靠近用戶的數(shù)據(jù)被用戶感受到的速度就越快
.
也就是近與快的關(guān)系
.
接著再讓我們拋開緩存先不說
,
來說說
CDN
和鏡像的問題
,CDN
的英文名字叫
CDN,
中文名字一般還是
CDN(
請(qǐng)換個(gè)調(diào)朗誦
).
呵呵
,CDN
中文名字是內(nèi)
容分布網(wǎng)絡(luò)
,
簡單來說就是把內(nèi)容分布出去
,
比如放到全國幾個(gè)地方
,
舉例來說做一個(gè)圖片服務(wù)
,
上海的用戶請(qǐng)求某個(gè)圖片服務(wù)器
,
那么只需要返回某個(gè)離上海最近
的
CDN
節(jié)點(diǎn)上的圖片
,
而不需要路由到北京或者云南的節(jié)點(diǎn)上去取數(shù)據(jù)
,
您要問為啥呢
,
因?yàn)榭彀?
,
上海的用戶訪問北京節(jié)點(diǎn)的數(shù)據(jù)顯然在路由層次上
,
網(wǎng)絡(luò)時(shí)間
消耗上都要多出很多
,
這說明啥呀
,
還是那個(gè)理兒
:
近就會(huì)快啊
一般來說
CDN
都是放一些圖片
,
視頻
,
文檔之類的數(shù)據(jù)
,
那么元數(shù)據(jù)呢
,
放一塊兒
,
當(dāng)然也不是
,
這時(shí)候可以用鏡像來解決元數(shù)據(jù)的問題
,
于是變成了上海的用戶訪問上海的鏡像
,
北京的用戶訪問北京的鏡像
.
這還不是就地取材比較方便嘛
.
嗯
,
說到這里
,
想必大家對(duì)近和快的關(guān)系有了一定的認(rèn)識(shí)了
,
下面我們來看看如何把這種原理或者規(guī)則運(yùn)用到緩存中去
.
下面讓
ahuaxuan
和大家先調(diào)查一下離眼睛最近的是什么
,
顯示器
(
別跟我說是屏幕保護(hù)膜和鍵盤哈
,
鼠標(biāo)也不行
),
不過這些是硬件呀
,
那軟的
呢
,
非瀏覽器莫數(shù)了
.
也就是說如果我們把一些可以緩存在瀏覽器上的數(shù)據(jù)緩存到瀏覽器上
,
那就能加快我們的頁面響應(yīng)速度了
.
也就是說我們現(xiàn)在找到一個(gè)地方
,
也許可以放一點(diǎn)可以緩存的數(shù)據(jù)
.
下面我們要考察考察什么樣的數(shù)據(jù)可以緩存在瀏覽器上
,
以及緩存在瀏覽器上的一些優(yōu)缺點(diǎn)或者限制因素
什么樣的數(shù)據(jù)可以緩存在瀏覽器上
?
瀏覽器上無法就幾種數(shù)據(jù)
,html,css,js,image,
等
.
那么接著我們來看看他們的變化特性
,
html
數(shù)據(jù)很多情況下是動(dòng)態(tài)的
,
但是也有很多情況下是某個(gè)時(shí)間段內(nèi)可以是靜態(tài)的
.
Css
一般是靜態(tài)的
Js
一般也是靜態(tài)的
Image
一般也是靜態(tài)的
.
喲
,
看上去后幾者基本都可以緩存在瀏覽器
,
而
html
是否緩存要看
html
中數(shù)據(jù)的特性了
.
那么問題來了
,
瀏覽器是依據(jù)什么設(shè)置來緩存
html,
或者
css,
或者
js
的呢
.
答曰
,expires
或者
max-age.
Expires
代表該份數(shù)據(jù)緩存到瀏覽器上
,
直到某個(gè)時(shí)間點(diǎn)后過期
,
而
max-age
表示緩存在瀏覽器上直到某個(gè)時(shí)間段之后過期
.
對(duì)于靜態(tài)內(nèi)容:設(shè)置文件頭過期時(shí)間
Expires
的值為
“Never expire”
(永不過期)
動(dòng)態(tài)頁面
,
在代碼中添加
cache-control,
表示多少時(shí)間之后過期
,
如
:
response.setHeader("Cache-Control", "max-age=3600");
表示
1
個(gè)小時(shí)后過期
,
即在瀏覽器上緩存一個(gè)小時(shí)
.
但是這樣問題又來了
,
如果設(shè)置
10
天后過期
,
那我明天就要改變
,css,js
都變了
,
咋辦吶
,
答曰
,
加版本號(hào)吧
,
這樣瀏覽器就會(huì)重新加載新的
css
和
js
了
.
但是如果是動(dòng)態(tài)數(shù)據(jù)
,
那就沒有折了
,
所以動(dòng)態(tài)數(shù)據(jù)的
max-age
一般不推薦太大
,
否則啊
,
您吶
,
就得挨個(gè)通知您得用戶按一下
Ctril+F5
了
.
一般來說靜態(tài)數(shù)據(jù)需要緩存
,
我們一般通過
webserver(
如
apache,lighttpd
之流
),
只需要配置一下即可
,
而動(dòng)態(tài)數(shù)據(jù)是比較重
要的
,
因?yàn)槠涓淖兊闹芷诙?
,
而且只能由
servlet
容器或者
fastcgi
之類的服務(wù)器返回
,
所以其應(yīng)付大量并發(fā)的能力是有限的
.
那么這里理論上可能有
一個(gè)瓶頸
,
就是如果訪問的獨(dú)立用戶較多
,
那么這份動(dòng)態(tài)數(shù)據(jù)還是會(huì)被請(qǐng)求
1*
用戶數(shù)
= n
次
,
那么我們可以想象
,
一樣的請(qǐng)求對(duì)于我們的
servlet
容器或者
fastcgi
來說其實(shí)是多余的
,
我們可以想一個(gè)方法
,
把這些一樣的請(qǐng)求擋在
servlet
容器或者
fastcgi
進(jìn)程之前
.
正如在第一說中說到的
,
在瀏覽器和
servlet
容器或者
fastcgi
進(jìn)程之間
,
還有很大的空間可以發(fā)揮
,
在這一部分的緩存
,ahuaxuan
稱之為
webcache.
目前在
webcache
屆
,
最流行的估計(jì)就屬
squid
了
,
然后還有
varnish
等等
.
為了有一個(gè)比較直觀的感受
,
我們來看看下面這張圖唄
:
?
從這張圖上
,
我們可以看出
,
瀏覽器
1
在請(qǐng)求了一份數(shù)據(jù)之后
,
其實(shí)這份數(shù)據(jù)已經(jīng)在
webcache
上了
,
瀏覽器再來請(qǐng)求
2
的時(shí)候
,
請(qǐng)求到了
webcache
這層就返回了
,
這樣就降低了
servlet container
的壓力了
.
雖然說我們?cè)?
servlet
容器上也是可以建
page cache,
但是畢竟
servlet
本身的并發(fā)能力有限
.(
如何在
servlet container
上使用
page cache
見
:
http://www.iteye.com/topic/128458
)
而且更重要的是一般
webcache
的并發(fā)能力要比
servlet container
或者
fastcgi process
要高出很多
(
沒辦法
,
誰叫它是專業(yè)的
cache
呢
).
所以使用
webcache
也能夠提供更高訪問量的服務(wù)
.
一舉多得
,
何樂而不為呢
.
但是聲明一下
,
您吶
,
別以為上
面這種方式是標(biāo)準(zhǔn)方式
,
我們還有
webserver,
負(fù)載均衡器等等
,
上圖只是為了便于說明本文的論點(diǎn)
,
而且互連網(wǎng)需求和解決方案層出不窮
,
切不可以胡搬
亂套
,
還是要分析分析再分析
,
思考
,
思科再思考
.
說到這里即使以前沒有接觸過得筒子大概也明白了
web cache
得作用了
.
下面我們?cè)賮砜纯慈绾问褂?
web cache
呢
,
呵呵
,
其實(shí)和瀏覽器上緩存數(shù)據(jù)得方式一樣
.
也是通過在
response header
中指定
expires
或者
max-age
來實(shí)現(xiàn)的
.(
但是據(jù)
ahuaxuan
觀察在使用
squid
的時(shí)候有一個(gè)要求
,
瀏覽器的請(qǐng)求必須滿足
http
的語義
,
也就是說只有
method=get
的時(shí)候
web cache
才能緩存數(shù)據(jù)
,
如果是
post,
那么
web cache
認(rèn)為這個(gè)是一個(gè)創(chuàng)建數(shù)據(jù)的請(qǐng)求
,
并不會(huì)緩存其返回結(jié)果
.)
Squid,
如果您要系統(tǒng)的學(xué)習(xí)
squid,
請(qǐng)看
:
http://www.squid-cache.org/
?????
http://blog.s135.com/book/squid/
補(bǔ)充
,
在有些情況下
,web cache
中的數(shù)據(jù)很有可能是有狀態(tài)的
.
比如根據(jù)瀏覽器的
locale
返回不同的數(shù)據(jù)
,
那么雖然訪問的
url
是一樣的
,
但是返回的值卻是不一樣的
,
咋辦
呢
,
別擔(dān)心
,
我們有
vary,
只要在
response
里指定
vary
參數(shù)為
accept-language
就
ok.
您也可以指定為
cookie
中的值
,
這
就完全看您的需要了
.
如果您還是不明白
vary
的作用
,
請(qǐng)看
:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
總結(jié)
:
說到這里
,
關(guān)于近和快的話題也基本可以結(jié)束了
(
這個(gè)話題再寫下去就變成裹腳布了
).
所以一般情況下
,
我們可以認(rèn)為有如下事實(shí)
:”
近
==
快
”,
但是
近和快并不只是表現(xiàn)在人的體驗(yàn)上
,
如果換個(gè)角度
,
速度的感受者不是人
,
而是機(jī)器
,
那么我們也可以這么認(rèn)為
local cache
比
remote cache
更靠近
cpu,
所以
local cache
的速度更快
(
當(dāng)然他們的功能不是重疊的
,
各自適用的場(chǎng)景不一樣而已
,
而且很多情況下他們可以配合使用
,
在后續(xù)的文章中將會(huì)討論這個(gè)問題
).
如何在只使用
tomcat
的情況下
,
自動(dòng)緩存
js
和
css
或者
image
等文件
.
第一步
:
寫一個(gè)
filter,
可以根據(jù)路徑的正則來判斷該路徑的請(qǐng)求是否需要設(shè)置
max-age:
public class CacheFilter implements Filter{ private static transient Log logger = LogFactory.getLog(CacheFilter.class); private Integer cacheTime = 3600 * 24; private List<Pattern> patternList = new ArrayList<Pattern>(); private static ResourceBundle rb = ResourceBundle.getBundle("cache-pattern"); public void destroy() { } public void doFilter(ServletRequest rq, ServletResponse rqs, FilterChain fc) throws IOException, ServletException { fc.doFilter(rq, rqs); if (rq instanceof HttpServletRequest && rqs instanceof HttpServletResponse) { HttpServletRequest request = (HttpServletRequest) rq; HttpServletResponse response = (HttpServletResponse) rqs; if (matchPattern(request.getRequestURI())) { response.setHeader("Cache-Control", "max-age=" + cacheTime); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); sb.append(" set cache control for uri = ").append(request.getRequestURI()); sb.append(" and the cache time is ").append(cacheTime).append(" second"); logger.debug(sb.toString()); } } } else { if (logger.isWarnEnabled()) { logger.warn("---- the request instance is not instanceof HttpServletRequest ---"); logger.warn("---- the response instance is not instanceof HttpServletResponse ---"); } } } public void init(FilterConfig arg0) throws ServletException { Enumeration<String> keys = rb.getKeys(); while (keys.hasMoreElements()) { String p = keys.nextElement(); String value = rb.getString(p); patternList.add(Pattern.compile(value, Pattern.CASE_INSENSITIVE)); if (logger.isInfoEnabled()) { logger.info(">>>>>>>>>>> init the cache pattern " + value); } } if (arg0 != null) { String ct = arg0.getInitParameter("cache-time"); if (!"".equals(ct) && null != ct) { cacheTime = new Integer(ct); if (logger.isInfoEnabled()) { logger.info(">>>>>>>>>> the cache time is " + cacheTime); } } } } private boolean matchPattern(String url) { for (Pattern pattern : patternList) { if (pattern.matcher(url).matches()) { return true; } } return false; } public static void main(String [] args) throws ServletException { CacheFilter cf = new CacheFilter(); cf.init(null); System.out.println(cf.matchPattern("/css/prototype.CSS")); } }
?
?
?
?
?
?
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
