這篇文章主要為大家展示了“java緩存核心技術(shù)的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學習一下“java緩存核心技術(shù)的示例分析”這篇文章吧。
創(chuàng)新互聯(lián)主要從事網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)泉港,10多年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220
緩存對于每個開發(fā)者來說是相當熟悉了,為了提高程序的性能我們會去加緩存,但是在什么地方加緩存,如何加緩存呢?
假設(shè)一個網(wǎng)站,需要提高性能,緩存可以放在瀏覽器,可以放在反向代理服務(wù)器,還可以放在應(yīng)用程序進程內(nèi),同時可以放在分布式緩存系統(tǒng)中。
從用戶請求數(shù)據(jù)到數(shù)據(jù)返回,數(shù)據(jù)經(jīng)過了瀏覽器,cdn,代理服務(wù)器,應(yīng)用服務(wù)器,以及數(shù)據(jù)庫各個環(huán)節(jié)。每個環(huán)節(jié)都可以運用緩存技術(shù)。
從瀏覽器/客戶端開始請求數(shù)據(jù),通過 HTTP 配合 CDN 獲取數(shù)據(jù)的變更情況,到達代理服務(wù)器(Nginx)可以通過反向代理獲取靜態(tài)資源。
再往下來到應(yīng)用服務(wù)器可以通過進程內(nèi)(堆內(nèi))緩存,分布式緩存等遞進的方式獲取數(shù)據(jù)。如果以上所有緩存都沒有命中數(shù)據(jù),才會回源到數(shù)據(jù)庫。
緩存的請求順序是:用戶請求 → HTTP 緩存 → CDN 緩存 → 代理服務(wù)器緩存 → 進程內(nèi)緩存 → 分布式緩存 → 數(shù)據(jù)庫。
看來在技術(shù)的架構(gòu)每個環(huán)節(jié)都可以加入緩存,看看每個環(huán)節(jié)是如何應(yīng)用緩存技術(shù)的。
當用戶通過瀏覽器請求服務(wù)器的時候,會發(fā)起 HTTP 請求,如果對每次 HTTP 請求進行緩存,那么可以減少應(yīng)用服務(wù)器的壓力。
當?shù)谝淮握埱蟮臅r候,瀏覽器本地緩存庫沒有緩存數(shù)據(jù),會從服務(wù)器取數(shù)據(jù),并且放到瀏覽器的緩存庫中,下次再進行請求的時候會根據(jù)緩存的策略來讀取本地或者服務(wù)的信息。
一般信息的傳遞通過 HTTP 請求頭 Header 來傳遞。目前比較常見的緩存方式有兩種,分別是:
強制緩存
對比緩存
當瀏覽器本地緩存庫保存了緩存信息,在緩存數(shù)據(jù)未失效的情況下,可以直接使用緩存數(shù)據(jù)。否則就需要重新獲取數(shù)據(jù)。
這種緩存機制看上去比較直接,那么如何判斷緩存數(shù)據(jù)是否失效呢?這里需要關(guān)注 HTTP Header 中的兩個字段 Expires 和 Cache-Control。
Expires 為服務(wù)端返回的過期時間,客戶端第一次請求服務(wù)器,服務(wù)器會返回資源的過期時間。如果客戶端再次請求服務(wù)器,會把請求時間與過期時間做比較。
如果請求時間小于過期時間,那么說明緩存沒有過期,則可以直接使用本地緩存庫的信息。
反之,說明數(shù)據(jù)已經(jīng)過期,必須從服務(wù)器重新獲取信息,獲取完畢又會更新最新的過期時間。
這種方式在 HTTP 1.0 用的比較多,到了 HTTP 1.1 會使用 Cache-Control 替代。
Cache-Control 中有個 max-age 屬性,單位是秒,用來表示緩存內(nèi)容在客戶端的過期時間。
例如:max-age 是 60 秒,當前緩存沒有數(shù)據(jù),客戶端第一次請求完后,將數(shù)據(jù)放入本地緩存。
那么在 60 秒以內(nèi)客戶端再發(fā)送請求,都不會請求應(yīng)用服務(wù)器,而是從本地緩存中直接返回數(shù)據(jù)。如果兩次請求相隔時間超過了 60 秒,那么就需要通過服務(wù)器獲取數(shù)據(jù)。
需要對比前后兩次的緩存標志來判斷是否使用緩存。瀏覽器第一次請求時,服務(wù)器會將緩存標識與數(shù)據(jù)一起返回,瀏覽器將二者備份至本地緩存庫中。瀏覽器再次請求時,將備份的緩存標識發(fā)送給服務(wù)器。
服務(wù)器根據(jù)緩存標識進行判斷,如果判斷數(shù)據(jù)沒有發(fā)生變化,把判斷成功的 304 狀態(tài)碼發(fā)給瀏覽器。
這時瀏覽器就可以使用緩存的數(shù)據(jù)來。服務(wù)器返回的就只是 Header,不包含 Body。
下面介紹兩種標識規(guī)則:
在客戶端第一次請求的時候,服務(wù)器會返回資源最后的修改時間,記作 Last-Modified??蛻舳藢⑦@個字段連同資源緩存起來。
Last-Modified 被保存以后,在下次請求時會以 Last-Modified-Since 字段被發(fā)送。
當客戶端再次請求服務(wù)器時,會把 Last-Modified 連同請求的資源一起發(fā)給服務(wù)器,這時 Last-Modified 會被命名為 If-Modified-Since,存放的內(nèi)容都是一樣的。
服務(wù)器收到請求,會把 If-Modified-Since 字段與服務(wù)器上保存的 Last-Modified 字段作比較:
若服務(wù)器上的 Last-Modified 最后修改時間大于請求的 If-Modified-Since,說明資源被改動過,就會把資源(包括 Header+Body)重新返回給瀏覽器,同時返回狀態(tài)碼 200。
若資源的最后修改時間小于或等于 If-Modified-Since,說明資源沒有改動過,只會返回 Header,并且返回狀態(tài)碼 304。瀏覽器接受到這個消息就可以使用本地緩存庫的數(shù)據(jù)。
注意:Last-Modified 和 If-Modified-Since 指的是同一個值,只是在客戶端和服務(wù)器端的叫法不同。
客戶端第一次請求的時候,服務(wù)器會給每個資源生成一個 ETag 標記。這個 ETag 是根據(jù)每個資源生成的唯一 Hash 串,資源如何發(fā)生變化 ETag 隨之更改,之后將這個 ETag 返回給客戶端,客戶端把請求的資源和 ETag 都緩存到本地。
ETag 被保存以后,在下次請求時會當作 If-None-Match 字段被發(fā)送出去。
在瀏覽器第二次請求服務(wù)器相同資源時,會把資源對應(yīng)的 ETag 一并發(fā)送給服務(wù)器。在請求時 ETag 轉(zhuǎn)化成 If-None-Match,但其內(nèi)容不變。
服務(wù)器收到請求后,會把 If-None-Match 與服務(wù)器上資源的 ETag 進行比較:
如果不一致,說明資源被改動過,則返回資源(Header+Body),返回狀態(tài)碼 200。
如果一致,說明資源沒有被改過,則返回 Header,返回狀態(tài)碼 304。瀏覽器接受到這個消息就可以使用本地緩存庫的數(shù)據(jù)。
注意:ETag 和 If-None-Match 指的是同一個值,只是在客戶端和服務(wù)器端的叫法不同。
HTTP 緩存主要是對靜態(tài)數(shù)據(jù)進行緩存,把從服務(wù)器拿到的數(shù)據(jù)緩存到客戶端/瀏覽器。
如果在客戶端和服務(wù)器之間再加上一層 CDN,可以讓 CDN 為應(yīng)用服務(wù)器提供緩存,如果在 CDN 上緩存,就不用再請求應(yīng)用服務(wù)器了。并且 HTTP 緩存提到的兩種策略同樣可以在 CDN 服務(wù)器執(zhí)行。
CDN 的全稱是 Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。
讓我們來看看它是如何工作的吧:
客戶端發(fā)送 URL 給 DNS 服務(wù)器。
DNS 通過域名解析,把請求指向 CDN 網(wǎng)絡(luò)中的 DNS 負載均衡器。
DNS 負載均衡器將最近 CDN 節(jié)點的 IP 告訴 DNS,DNS 告之客戶端最新 CDN 節(jié)點的 IP。
客戶端請求最近的 CDN 節(jié)點。
CDN 節(jié)點從應(yīng)用服務(wù)器獲取資源返回給客戶端,同時將靜態(tài)信息緩存。注意:客戶端下次互動的對象就是 CDN 緩存了,CDN 可以和應(yīng)用服務(wù)器同步緩存信息。
CDN 接受客戶端的請求,它就是離客戶端最近的服務(wù)器,它后面會鏈接多臺服務(wù)器,起到了緩存和負載均衡的作用。
說完客戶端(HTTP)緩存和 CDN 緩存,我們離應(yīng)用服務(wù)越來越近了,在到達應(yīng)用服務(wù)之前,請求還要經(jīng)過負載均衡器。
雖說它的主要工作是對應(yīng)用服務(wù)器進行負載均衡,但是它也可以作緩存??梢园岩恍┬薷念l率不高的數(shù)據(jù)緩存在這里,例如:用戶信息,配置信息。通過服務(wù)定期刷新這個緩存就行了。
以 Nginx 為例,我們看看它是如何工作的:
用戶請求在達到應(yīng)用服務(wù)器之前,會先訪問 Nginx 負載均衡器,如果發(fā)現(xiàn)有緩存信息,直接返回給用戶。
如果沒有發(fā)現(xiàn)緩存信息,Nginx 回源到應(yīng)用服務(wù)器獲取信息。
另外,有一個緩存更新服務(wù),定期把應(yīng)用服務(wù)器中相對穩(wěn)定的信息更新到 Nginx 本地緩存中。
通過了客戶端,CDN,負載均衡器,我們終于來到了應(yīng)用服務(wù)器。應(yīng)用服務(wù)器上部署著一個個應(yīng)用,這些應(yīng)用以進程的方式運行著,那么在進程中的緩存是怎樣的呢?
進程內(nèi)緩存又叫托管堆緩存,以APC為例,同時會受到托管堆回收算法的影響。
由于其運行在內(nèi)存中,對數(shù)據(jù)的響應(yīng)速度很快,通常我們會把熱點數(shù)據(jù)放在這里。
在進程內(nèi)緩存沒有命中的時候,我們會去搜索進程外的緩存或者分布式緩存。這種緩存的好處是沒有序列化和反序列化,是最快的緩存。缺點是緩存的空間不能太大,對垃圾回收器的性能有影響。
這里我們需要關(guān)注幾個緩存的回收策略,具體的實現(xiàn)架構(gòu)的回收策略會有所不同,但大致的思路都是一致的:
FIFO(First In First Out):先進先出算法,最先放入緩存的數(shù)據(jù)最先被移除。
LRU(Least Recently Used):最近最少使用算法,把最久沒有使用過的數(shù)據(jù)移除緩存。
LFU(Least Frequently Used):最不常用算法,在一段時間內(nèi)使用頻率最小的數(shù)據(jù)被移除緩存。
在分布式架構(gòu)的今天,多應(yīng)用中如果采用進程內(nèi)緩存會存在數(shù)據(jù)一致性的問題。
這里推薦兩個方案:
消息隊列修改方案
Timer 修改方案
應(yīng)用在修改完自身緩存數(shù)據(jù)和數(shù)據(jù)庫數(shù)據(jù)之后,給消息隊列發(fā)送數(shù)據(jù)變化通知,其他應(yīng)用訂閱了消息通知,在收到通知的時候修改緩存數(shù)據(jù)。
為了避免耦合,降低復(fù)雜性,對“實時一致性”不敏感的情況下。每個應(yīng)用都會啟動一個 Timer,定時從數(shù)據(jù)庫拉取最新的數(shù)據(jù),更新緩存。
不過在有的應(yīng)用更新數(shù)據(jù)庫后,其他節(jié)點通過 Timer 獲取數(shù)據(jù)之間,會讀到臟數(shù)據(jù)。這里需要控制好 Timer 的頻率,以及應(yīng)用與對實時性要求不高的場景。
進程內(nèi)緩存有哪些使用場景呢?
場景一:只讀數(shù)據(jù),可以考慮在進程啟動時加載到內(nèi)存。當然,把數(shù)據(jù)加載到類似 redis 這樣的進程外緩存服務(wù)也能解決這類問題。
場景二:高并發(fā),可以考慮使用進程內(nèi)緩存,例如:秒殺。
說完進程內(nèi)緩存,自然就過度到進程外緩存了。與進程內(nèi)緩存不同,進程外緩存在應(yīng)用運行的進程之外,它擁有更大的緩存容量,并且可以部署到不同的物理節(jié)點,通常會用分布式緩存的方式實現(xiàn)。
分布式緩存是與應(yīng)用分離的緩存服務(wù),最大的特點是,自身是一個獨立的應(yīng)用/服務(wù),與本地應(yīng)用隔離,多個應(yīng)用可直接共享一個或者多個緩存應(yīng)用/服務(wù)。
既然是分布式緩存,緩存的數(shù)據(jù)會分布到不同的緩存節(jié)點上,每個緩存節(jié)點緩存的數(shù)據(jù)大小通常也是有限制的。
數(shù)據(jù)被緩存到不同的節(jié)點,為了能方便的訪問這些節(jié)點,需要引入緩存代理,類似 Twemproxy。他會幫助請求找到對應(yīng)的緩存節(jié)點。
同時如果緩存節(jié)點增加了,這個代理也會只能識別并且把新的緩存數(shù)據(jù)分片到新的節(jié)點,做橫向的擴展。
為了提高緩存的可用性,會在原有的緩存節(jié)點上加入 Master/Slave 的設(shè)計。當緩存數(shù)據(jù)寫入 Master 節(jié)點的時候,會同時同步一份到 Slave 節(jié)點。
一旦 Master 節(jié)點失效,可以通過代理直接切換到 Slave 節(jié)點,這時 Slave 節(jié)點就變成了 Master 節(jié)點,保證緩存的正常工作。
每個緩存節(jié)點還會提供緩存過期的機制,并且會把緩存內(nèi)容定期以快照的方式保存到文件上,方便緩存崩潰之后啟動預(yù)熱加載。
當緩存做成分布式的時候,數(shù)據(jù)會根據(jù)一定的規(guī)律分配到每個緩存應(yīng)用/服務(wù)上。
如果我們把這些緩存應(yīng)用/服務(wù)叫做緩存節(jié)點,每個節(jié)點一般都可以緩存一定容量的數(shù)據(jù),例如:Redis 一個節(jié)點可以緩存 2G 的數(shù)據(jù)。
如果需要緩存的數(shù)據(jù)量比較大就需要擴展多個緩存節(jié)點來實現(xiàn),這么多的緩存節(jié)點,客戶端的請求不知道訪問哪個節(jié)點怎么辦?緩存的數(shù)據(jù)又如何放到這些節(jié)點上?
緩存代理服務(wù)已經(jīng)幫我們解決這些問題了,例如:Twemproxy 不但可以幫助緩存路由,同時可以管理緩存節(jié)點。
這里有介紹三種緩存數(shù)據(jù)分片的算法,有了這些算法緩存代理就可以方便的找到分片的數(shù)據(jù)了。
Hash 表是最常見的數(shù)據(jù)結(jié)構(gòu),實現(xiàn)方式是,對數(shù)據(jù)記錄的關(guān)鍵值進行 Hash,然后再對需要分片的緩存節(jié)點個數(shù)進行取模得到的余數(shù)進行數(shù)據(jù)分配。
例如:有三條記錄數(shù)據(jù)分別是 R1,R2,R3。他們的 ID 分別是 01,02,03,假設(shè)對這三個記錄的 ID 作為關(guān)鍵值進行 Hash 算法之后的結(jié)果依舊是 01,02,03。
我們想把這三條數(shù)據(jù)放到三個緩存節(jié)點中,可以把這個結(jié)果分別對 3 這個數(shù)字取模得到余數(shù),這個余數(shù)就是這三條記錄分別放置的緩存節(jié)點。
Hash 算法是某種程度上的平均放置,策略比較簡單,如果要增加緩存節(jié)點,對已經(jīng)存在的數(shù)據(jù)會有較大的變動。
一致性 Hash 是將數(shù)據(jù)按照特征值映射到一個首尾相接的 Hash 環(huán)上,同時也將緩存節(jié)點映射到這個環(huán)上。
如果要緩存數(shù)據(jù),通過數(shù)據(jù)的關(guān)鍵值(Key)在環(huán)上找到自己存放的位置。這些數(shù)據(jù)按照自身的 ID 取 Hash 之后得到的值按照順序在環(huán)上排列。
如果這個時候要插入一條新的數(shù)據(jù)其 ID 是 115,那么就應(yīng)該插入到如下圖的位置。
同理如果要增加一個緩存節(jié)點 N4 150,也可以放到如下圖的位置。
這種算法對于增加緩存數(shù)據(jù),和緩存節(jié)點的開銷相對比較小。
這種方式是按照關(guān)鍵值(例如 ID)將數(shù)據(jù)劃分成不同的區(qū)間,每個緩存節(jié)點負責一個或者多個區(qū)間。跟一致性哈希有點像。
例如:存在三個緩存節(jié)點分別是 N1,N2,N3。他們用來存放數(shù)據(jù)的區(qū)間分別是,N1(0, 100], N2(100, 200], N3(300, 400]。
那么數(shù)據(jù)根據(jù)自己 ID 作為關(guān)鍵字做 Hash 以后的結(jié)果就會分別對應(yīng)放到這幾個區(qū)域里面了。
以上是“java緩存核心技術(shù)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)頁題目:java緩存核心技術(shù)的示例分析
地址分享:http://chinadenli.net/article36/pgppsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司、手機網(wǎng)站建設(shè)、微信公眾號、網(wǎng)站收錄、商城網(wǎng)站、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)