A. 系統緩存是什麼來的有何作用
分類: 電腦/網路 >> 操作系統/系統故障
解析:
理解緩存
操作系統的任務主要是合理地調配系統的各種資源,為各種程序的運行提供環境,它可以看作是硬體和應用軟體之間的一個媒介。其中對內存的管理是系統的最主要的職責,怎麼樣使有限的內存用在刀刃上,怎麼要保證系統本身所需的內存(以防止死機,在win2000和winxp里這一點已經做的非常好了),怎樣克服各種硬體連接的瓶頸。
本文主要就這種硬體連接的瓶頸問題展開一些討論。大家知道計算機的主要硬體,硬碟,內存和處理器之間的速度是不一樣的,其中處理器的速度是非常快的,內存次之,而硬碟的速度是很慢的(相對於處理器來說),一件任務的處理要通過處理器給出的指令,把相關數據從硬碟里調出來,到內存,在內存和處理器之間還會有許多數據的傳輸,內存本身不能處理數據,要通過處理器來處理,當他們一起工作的時候,由於處理器和內存工作得快,它們常在把事做完了沒事做了,要等硬碟,這樣就大大降低了系統的整體性能,不能發揮所有硬體的性能。為了解決這個問題,一個優秀的操作系統必然要有「緩存」來作為這些硬體之間的一個中間站,來緩和這種矛盾,從而一定程度上提高系統的性能,「緩存」處理的越好,系統的性能發揮的越好。所以研究「緩存」就有了它的意義。
看了上面的內容,以前對「緩存」沒有認識的朋友應該理解它了。理解之後馬上可以應用的地方就是在自購兼容機的時候。大家大可不必去追趕潮流,買什麼P4處理器,而應該買一個快一點的硬碟,比如買個7200轉的(或更快的),以減少瓶頸的矛盾。處理器嗎,買賽揚好了,一般是沒問題的,處理器大多數時候是閑著的,有時處理很多個大任務時可能會有些緊張,注意避免就可以了。
從某種角度講,內存本身是硬碟和處理器之間的一個緩存,它的作用是緩解硬碟和處理器之間的尖銳矛盾的。當它被作為一個固定的部件後,它本身也成了需要用緩存來緩解瓶頸的對象。它對處理器和硬碟夾在中間,是他們的必經之路,硬碟與處理器之間的關系成了硬碟與內存和內存與處理器之間的雙重關系。所以上面提到的瓶頸問題的處理歸結為對內存的優化,即怎樣處理好硬碟與內存之間的緩存很處理器與內存之間的緩存。
對於一個想了解操作系統的人來說,能夠理解「緩存」對對它做適當的優化是比不可少的一節課。另外再不從一下,緩存的概念是很廣泛的,這里專指內存的緩存。
緩存的優化
操作系統本身已經有了很多優化措施,而我們只能在它的優化措施的基礎上根據我們的實際情況來優化。
1,最「著名」的緩存是頁面文件,這個倒不是緩解速度的,而是緩解容量的,在速度上,硬碟不如內存,但是容量上,內存是不可能跟硬碟比的,當你運行一個程序需要大量數據,佔有大量內存時,內存就要被塞滿,怎麼辦呢?把那些暫時不用的放到硬碟里去,因為處理器總是只調用處理一個任務所需的數據,其他的准備的數據(就是那些可能要用的,但暫時還不用的)可以先放一放,如果內存放不下,就只好放到硬碟了。但是這樣做是有代價的,當放到內存的數據重新要被使用時,你就得等很長時間等系統把在硬碟中得數據調上來。其實你可以感受到系統的這些動作,比如你打開IE或Office,第一次打開是很慢的,但是關閉後馬上再打開就快很多,這是因為這時數據還沒被系統「請」出內存,系統從內存中直接取得數據自然快了;另一個情況,當你開了一個photoshop這樣的大軟體,這時打開Office要比平時還慢一點,這是因為內存本來被photoshop佔領著,要調入Office的數據到內存就必須把photoshop的數據「請」出內存,多了這個過程,打開自然要慢一些。
優化頁面文件,可以做一下幾條:
1)把頁面文件放到系統盤之外,這樣做主要是為了保持頁面文件的連續性,硬碟讀取數據是靠磁頭在磁性物質上「讀」得到數據的,頁面文件放在物理上的不同區域,磁頭就要跳來跳去,自然不利於效率。系統盤文件眾多,頁面文件幾乎肯定不連續。所以要把它移到其他盤。要提醒一點,當你移動好頁面文件後,要把原來的刪除掉,系統不會自動刪除。
2)如果有兩個硬碟,把頁面文件放在轉速快的那個,原因上面已講了很多遍了。
3)最大最小頁面文件的設置原則。有很多人建議將這兩個值設置成相等的,我不知道他們是那裡來的依據,其實這樣設置是不合理的。我們先要知道他們兩個值的意義。一般情況下,內存是不會真的「塞滿」,它會在內存儲量到達一定程度時自動將一部分暫時不用的數據放到硬碟,最小頁面文件是所說的「一定程度」的具體比例的決定因素,最小頁面文件大,比例就低,反之則相反;最大頁面文件是極限值,有時你開了很多程序,內存和最小頁面文件都滿了,就自動溢出到最大頁面文件。所以,將兩者設為一樣大是不合理的。最小頁面文件要小一些,這樣能夠在內存中盡可能存更多的數據,效率就高,最大頁面文件大一些,以免出現「全滿」的的情況。
4)winxp現在支持4G內存,哪怕你有5,6百M的內存,你都不需頁面文件了,這時可以把頁面文件禁掉。到注冊表編輯器HKEY_LOCAL_MACHINE\System\CurrentControlSet\ControlSession Manager \Memory Management下,找到DisablePagingExecutive(禁用頁面文件)設其值為1。
5)在同上的注冊表編輯器位置上有個ClearPageFileAtShutdown(關機時清除頁面文件),這里所說的「清除」頁面文件(即虛擬內存)並非是指從硬碟上完全刪除pagefile .sys這個文件,而是對其進行「清洗」和整理,從而為下次啟動Win2K更好地利用虛擬內存作好准備。這樣做還有利於安全,頁面文件上的殘留的數據是可以用特殊的工具讀到的,而這些數據你可能並不想讓人知道。這樣做的代價是關閉系統時間會加長。 將其值設為1即可。
6)學過C的朋友們應該對操作內存有個概念,一個任務完成後,要用free函數來釋放內存,但有很多軟體在設計的時候,並沒有在所有環節都這樣做,這會造成無用的數據占據內存,對這種情況可以使用一些內存優化軟體,讓這鍾軟體來完成釋放內存的動作。
2,下面介紹和優化一些不著名的緩存:
1)內存讀取硬碟數據要經過一個系統緩存(system cache),它的位置是在內存的特定區域,它是用來緩解硬碟與內存之間的速度不平衡的。它是以犧牲內存資源來換取從硬碟讀取數據時的速度的,有了這塊緩存,系統能從硬碟預讀所需的數據,減少系統等待的時間。如果你的內存很大,比如5,6百M,那麼你除了可以採取上面說的關閉頁面文件的方法外,還可以起用打的系統緩存。做法如下,進入注冊表編輯器: HKEY_LOCAL_MACHINE\System\CurrentControlSet\ControlSession Manager \Memory Management,找到LargeSystemCache(啟用大的系統緩存),將它的值設為1就可以了。
這樣設置了後,systemcache從4M增加到8M,再win2000和winxp中,這個值是動態的,如果內存不足,systemcache占據的空間可以自動相應調整。
2)處理器從內存讀取數據的緩存是什麼呢?是二級數據高級緩存(緩沖),同樣它也要在內存中佔一個空間,所以最好是有了大內存之後再設置這個值。也需再注冊表裡設置,方法如下:進入 HKEY_LOCAL_MACHINE\System\CurrentControlSet\ControlSession Manager \Memory Management ,找到 SecondLevelDataCache,默認為256,大內存設為512。
好了經過了上面的介紹,我想對朋友們來說最重要收獲的是加深了對緩存和操作系統的認識,至於優化的方法,我得承認一般的朋友是用不著去做的,當然「玩」一下也是很有意思的。
B. SpringBoot進階之整合Shiro實現緩存和會話管理
大家好,一直以來我都本著用最通俗的話理解核心的知識點, 我認為所有的難點都離不開 「基礎知識」 的鋪墊。目前正在出一個 SpringBoot 長期系列教程,從入門到進階, 篇幅會較多~
「大佬可以繞過 ~」
如果你是一路看過來的,很高興你能夠耐心看完。之前帶大家學了 Springboot 基礎部分,對基本的使用有了初步的認識, 接下來的幾期內容將會帶大家進階使用,會先講解基礎 中間件 的使用和一些場景的應用,或許這些技術你聽說過,沒看過也沒關系,我會帶大家一步一步的入門,耐心看完你一定會有 收獲 ~
上期帶大家學習了 Shiro 中如何進行許可權認證,本期將帶大家學習 Shiro 中如何進行 緩存和會話管理 ,最後我們將做一個在線用戶管理以及強制下線用戶的功能,同樣的,我們集成到 Springboot 中。
首先我們要明白使用緩存的原因,為啥要用它 還記得之前帶大家實現的 用戶認證 和 許可權認證 嗎,那裡我使用了 MockUser ,真實場景中是要去數據查詢的,這樣一來就會產生耗時,請求多的時候資料庫肯定忙不過來了,所以我們需要使用緩存來提高程序響應速度
緩存使用 Redis ,下面就帶大家整一下:
修改 ShiroConfig ,添加方法
這樣就可以了,大家可以把測試獲取用戶的地方改成資料庫獲取,看下 控制台 sql日誌會明顯減少,因為有一部分是從緩存拿的
這部分功能還是比較好玩的,學完可以自由發揮做一個房間功能,可以加入可以踢人,下面我們就開整
修改 ShiroConfig ,添加方法,因為我們使用的是 Redis 緩存
實現 SessionListener
最後同樣的,想要開啟需要我們注入到 Manager 中:
我們先定義一個類,用來記錄在線用戶:
那麼怎麼獲取呢?我們定義一個方法,大家實踐中可以抽到 Service 層,這里方便演示,我直接寫到控制器里
如果你看誰不爽,可以直接讓他下線,hhh~
是不是很簡單,這里就不演示了,大家自行試試
本期內容就到這里結束了,總結一下,本節主要講了 Shiro 如何進行緩存以及如何進行用戶會話管理,大家可以舉一反三,做一些小功能嘗試嘗試
下期給大家講講 Shiro 中如何整合 JWT ,這個大家應該不陌生,如果不知道啥是 JWT 也沒關系,我會帶大家一步一步入門,下期也是 Shiro 系列的終極篇,內容可能有點多,耐心看完哦。歡迎加群一起學習交流 ~
C. 緩存-redis 三種模式搭建和運行原理
標簽: redis 緩存 主從 哨兵 集群
本文簡單的介紹redis三種模式在linux的安裝部署和數據存儲的總結,希望可以相互交流相互提升。
對於Centos7在安裝redis之前需要進行一些常用工具的安裝:
關閉防火牆
正式安裝redis
在redis進行maketest時候會出現一系列的異常,有如下解決方案:
用redis-server啟動一下redis,做一些實驗沒什麼意義。
要把redis作為一個系統的daemon進程去運行的,每次系統啟動,redis進程一起啟動,操作不走如下:
RDB和AOF是redis的一種數據持久化的機制。 持久化 是為了避免系統在發生災難性的系統故障時導致的系統數據丟失。我們一般會將數據存放在本地磁碟,還會定期的將數據上傳到雲伺服器。
RDB 是redis的snapshotting,通過redis.conf中的save配置進行設置,如 save 60 1000:
AOF 是以appendonly方式進行數據的儲存的,開啟AOF模式後,所有存進redis內存的數據都會進入os cache中,然後默認1秒執行一次fsync寫入追加到appendonly.aof文件中。一般我們配置redis.conf中的一下指令:
AOF和RDB模式我們一般在生產環境都會打開,一般而言,redis服務掛掉後進行重啟會優先家在aof中的文件。
當啟動一個slave node的時候,它會發送一個PSYNC命令給master node,如果這是slave node重新連接master node,那麼master node僅僅會復制給slave部分缺少的數據;否則如果是slave node第一次連接master node,那麼會觸發一次full resynchronization;
開始full resynchronization的時候,master會啟動一個後台線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的所有寫命令緩存在內存中。RDB文件生成完畢之後,master會將這個RDB發送給slave,slave會先寫入本地磁碟,然後再從本地磁碟載入到內存中。然後master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。
slave node如果跟master node有網路故障,斷開了連接,會自動重連。master如果發現有多個slave node都來重新連接,僅僅會啟動一個rdb save操作,用一份數據服務所有slave node。
從redis 2.8開始,就支持主從復制的斷點續傳,如果主從復制過程中,網路連接斷掉了,那麼可以接著上次復制的地方,繼續復制下去,而不是從頭開始復制一份。
master node會在內存中常見一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。如果master和slave網路連接斷掉了,slave會讓master從上次的replica offset開始繼續復制,但是如果沒有找到對應的offset,那麼就會執行一次resynchronization。
master在內存中直接創建rdb,然後發送給slave,不會在自己本地落地磁碟了,可以有如下配置:
slave不會過期key,只會等待master過期key。如果master過期了一個key,或者通過LRU淘汰了一個key,那麼會模擬一條del命令發送給slave。
在redis.conf配置文件中,上面的參數代表至少需要3個slaves節點與master節點進行連接,並且master和每個slave的數據同步延遲不能超過10秒。一旦上面的設定沒有匹配上,則master不在提供相應的服務。
sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了 is-master-down-after-milliseconds 指定的毫秒數之後,就主觀認為master宕機
sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了 quorum 指定數量的其他哨兵也認為那個master是sdown了,那麼就認為是odown了,客觀認為master宕機
如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認為不適合選舉為master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個slave來做切換,這個slave還得得到majority哨兵的授權,才能正式執行切換;
(2)SENTINEL RESET *,在所有sentinal上執行,清理所有的master狀態
(3)SENTINEL MASTER mastername,在所有sentinal上執行,查看所有sentinal對數量是否達成了一致
4.3.2 slave的永久下線
讓master摘除某個已經下線的slave:SENTINEL RESET mastername,在所有的哨兵上面執行.
redis的集群模式為了解決系統的橫向擴展以及海量數據的存儲問題,如果你的數據量很大,那麼就可以用redis cluster。
redis cluster可以支撐N個redis master,一個master上面可以掛載多個slave,一般情況我門掛載一個到兩個slave,master在掛掉以後會主動切換到slave上面,或者當一個master上面的slave都掛掉後,集群會從其他master上面找到冗餘的slave掛載到這個master上面,達到了系統的高可用性。
2.1 redis cluster的重要配置
2.2 在三台機器上啟動6個redis實例
將上面的配置文件,在/etc/redis下放6個,分別為: 7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf
每個啟動腳本內,都修改對應的埠號
2.3 創建集群
解決辦法是 先安裝rvm,再把ruby版本提升至2.3.3
使用redis-trib.rb命令創建集群
--replicas: 表示每個master有幾個slave
redis-trib.rb check 192.168.31.187:7001 查看狀體
3.1 加入新master
以上相同配置完成後,設置啟動腳本進行啟動;然後用如下命令進行node節點添加:
3.2 reshard一些數據過去
3.3 添加node作為slave
3.4 刪除node
D. 存儲器層次結構中的緩存
《深入理解計算機系統》p422
6.1 存儲器層次結構中的緩存
一般而言,高速緩存( cache ,讀作「 cash 」)是一個小而快速的存儲設備,它作為存儲在更大、也更慢的設備中的數據對象的緩沖區域。使用高速緩存的過程稱為緩存( caching ,讀作「 cashing 」)。存儲器層次結構的中心思想是,對於每個 k ,位於 k 層的更快更小的存儲設備作為位於 k 十1層的更大更慢的存儲設備的緩存。換句話說,層次結構中的每一層都緩存來自較低一層的數據對象。例如,本地磁碟作為通過網路從遠程磁碟取出的文件(例如 Web 頁面)的緩存,主存作為本地磁碟上數據的緩存,依此類推,直到最小的緩存—— CPU 寄存器組。圖6-22展示了存儲器層次結構中緩存的一般性概念。第 k 十1層的存儲器被劃分成連續的數據對象組塊( chunk ),稱為塊( block )。每個塊都有一個唯一的地址或名字,使之區別於其他的塊。塊可以是固定大小的(通常是這樣的),也可以是可變大小的(例如存儲在 Web 伺服器上的遠程 HTML 文件)。例如,圖6-22中第 k 十1層存儲器被劃分成16個大小固定的塊,編號為0~15。
類似地,第 k 層的存儲器被劃分成較少的塊的集合,每個塊的大小與 k 十1層的塊的大小一樣。在任何時刻,第 k 層的緩存包含第 k 十1層塊的一個子集的副本。例如,在圖6-22中,第 k 層的緩存有4個塊的空間,當前包含塊4、9、14和3的副本。
數據總是以塊大小為傳送單元( transfer unit )在第 k 層和第 k +1層之間來回復制的。雖然在層次結構中任何一對相鄰的層次之間塊大小是固定的,但是其他的層次對之間可以有不同的塊大小。例如,在圖6-21中,L1和 LO 之間的傳送通常使用的是1個字大小的塊。L2和L1之間(以及I3和I2之間、L4和I3之間)的傳送通常使用的是幾十個位元組的
塊。而L5和L4之間的傳送用的是大小為幾百或幾千位元組的塊。一般而言,層次結構中較低層(離 CPU 較遠)的設備的訪問時間較長,因此為了補償這些較長的訪問時間,傾向於使用較大的塊。
1. 緩存命中
當程序需要第 k 十1層的某個數據對象 d 時,它首先在當前存儲在第 k 層的一個塊中查找 d 。如果 d 剛好緩存在第 k 層中,那麼就是我們所說的緩存命中( cache hit )。該程序直接從第 k 層讀取 d ,根據存儲器層次結構的性質,這要比從第 k +1層讀取 d 更快。例如,一個有良好時間局部性的程序可以從塊14中讀出一個數據對象,得到一個對第 k 層的緩存命中。
2. 緩存不命中
另一方面,如果第 k 層中沒有緩存數據對象 d ,那麼就是我們所說的緩存不命中( cache miss )。當發生緩存不命中時,第 k 層的緩存從第 k 十1層緩存中取出包含 d 的那個塊,如果第 k 層的緩存已經滿了,可能就會覆蓋現存的一個塊。
覆蓋一個現存的塊的過程稱為替換( replacing )或驅逐( evicting )這個塊。被驅逐的這個塊有時也稱為犧牲塊( victim block )。決定該替換哪個塊是由緩存的替換策略( replace — ment policy )來控制的。例如,一個具有隨機替換策略的緩存會隨機選擇一個犧牲塊。一個具有最近最少被使用 LRU )替換策略的緩存會選擇那個最後被訪問的時間距現在最遠的塊。
在第 k 層緩存從第 k 十1層取出那個塊之後,程序就能像前面一樣從第 k 層讀出 d 了。例如,在圖6-22中,在第 k 層中讀塊12中的一個數據對象,會導致一個緩存不命中,因為塊12當前不在第 k 層緩存中。一旦把塊12從第 k 十1層復制到第 k 層之後,它就會保持在那裡,等待稍後的訪問。
3. 緩存不命中的種類
區分不同種類的緩存不命中有時候是很有幫助的。如果第 k 層的緩存是空的,那麼對
任何數據對象的訪問都會不命中。一個空的緩存有時被稱為冷緩存( cold cache ),此類不命中稱為強制性不命中( compulsory miss )或冷不命中( cold miss )。冷不命中很重要,因為它們通常是短暫的事件,不會在反復訪問存儲器使得緩存暖身( warmed up )之後的穩定狀態中出現。
只要發生了不命中,第 k 層的緩存就必須執行某個放置策略( placement policy ),確定把它從第 k 十1層中取出的塊放在哪裡。最靈活的替換策略是允許來自第 k +1層的任何塊放在第 k 層的任何塊中。對於存儲器層次結構中高層的緩存(靠近 CPU ),它們是用硬體來實現的,而且速度是最優的,這個策略實現起來通常很昂貴,因為隨機地放置塊,定位起來代價很高。
因此,硬體緩存通常使用的是更嚴格的放置策略,這個策略將第 k 十1層的某個塊限制放置在第 k 層塊的一個小的子集中(有時只是一個塊)。例如,在圖6-22中,我們可以確定第 k 十1層的塊 i 必須放置在第 k 層的塊( i mod 4)中。例如,第 k 十1層的塊0、4、8和12會映射到第 k 層的塊0;塊1、5、9和13會映射到塊1;依此類推。注意,圖6-22中的示例緩存使用的就是這個策略。
這種限制性的放置策略會引起一種不命中,稱為沖突不命中( conflict miss ),在這種情況中,緩存足夠大,能夠保存被引用的數據對象,但是因為這些對象會映射到同一個緩存塊,緩存會一直不命中。例如,在圖6-22中,如果程序請求塊0,然後塊8,然後塊0,然後塊8,依此類推,在第 k 層的緩存中,對這兩個塊的每次引用都會不命中,即使這個緩存總共可以容納4個塊。
程序通常是按照一系列階段(如循環)來運行的,每個階段訪問緩存塊的某個相對穩定不變的集合。例如,一個嵌套循環可能會反復地訪問同一個數組的元素。這個塊的集合稱為這個階段的工作集( working set )。當工作集的大小超過緩存的大小時,緩存會經歷容量不命中( capacity miss )。換句話說就是,緩存太小了,不能處理這個工作集。
4. 緩存管理
正如我們提到過的,存儲器層次結構的本質是,每一層存儲設備都是較低一層的緩存。在每一層上,某種形式的邏輯必須管理緩存。這里,我們的意思是指某個東西要將緩存劃分成塊,在不同的層之間傳送塊,判定是命中還是不命中,並處理它們。管理緩存的邏輯可以是硬體、軟體,或是兩者的結合。
例如,編譯器管理寄存器文件,緩存層次結構的最高層。它決定當發生不命中時何時發射載入,以及確定哪個寄存器來存放數據。L1、L2和L3層的緩存完全是由內置在緩存中的硬體邏輯來管理的。在一個有虛擬內存的系統中, DRAM 主存作為存儲在磁碟上的數據塊的緩存,是由操作系統軟體和 CPU 上的地址翻譯硬體共同管理的。對於一個具有像 AFS 這樣的分布式文件系統的機器來說,本地磁碟作為緩存,它是由運行在本地機器上的 AFS 客戶端進程管理的。在大多數時候,緩存都是自動運行的,不需要程序採取特殊的或顯式的行動。
6.3.2 存儲器層次結構概念小結
概括來說,基於緩存的存儲器層次結構行之有效,是因為較慢的存儲設備比較快的存儲設備更便宜,還因為程序傾向於展示局部性:
1)利用時間局部性: 由於時間局部性,同一數據對象可能會被多次使用。一旦一個數據對象在第一次不命中時被復制到緩存中,我們就會期望後面對該目標有一系列的訪問命中。因為緩存比低一層的存儲設備更快,對後面的命中的服務會比最開始的不命中快很多。
2)利用空間局部性: 塊通常包含有多個數據對象。由於空間局部性,我們會期望後面對該塊中其他對象的訪問能夠補償不命中後復制該塊的花費。現代系統中到處都使用了緩存。正如從圖6-23中能夠看到的那樣, CPU 晶元、操作系統、分布式文件系統中和萬維網上都使用了緩存。各種各樣硬體和軟體的組合構成和管理著緩存。注意,圖6-23中有大量我們還未涉及的術語和縮寫。在此我們包括這些術語和縮寫是為了說明緩存是多麼的普遍。
E. 緩存的緩存分類
靜態頁面的緩存可能有2種形式:其實主要區別就是CMS是否自己負責關聯內容的緩存更新管理。
1、靜態緩存:是在新內容發布的同時就立刻生成相應內容的靜態頁面,比如:2003年3月22日,管理員通過後台內容管理界面錄入一篇文章後,並同步更新相關索引頁上的鏈接。
2、動態緩存:是在新內容發布以後,並不預先生成相應的靜態頁面,直到對相應內容發出請求時,如果前台緩存伺服器找不到相應緩存,就向後台內容管理伺服器發出請求,後台系統會生成相應內容的靜態頁面,用戶第一次訪問頁面時可能會慢一點,但是以後就是直接訪問緩存了。
靜態緩存的缺點:
復雜的觸發更新機制:這兩種機制在內容管理系統比較簡單的時候都是非常適用的。但對於一個關系比較復雜的網站來說,頁面之間的邏輯引用關系就成為一個非常非常復雜的問題。最典型的例子就是一條新聞要同時出現在新聞首頁和相關的3個新聞專題中,在靜態緩存模式中,每發一篇新文章,除了這篇新聞內容本身的頁面外,還需要系統通過觸發器生成多個新的相關靜態頁面,這些相關邏輯的觸發也往往就會成為內容管理系統中最復雜的部分之一。
舊內容的批量更新: 通過靜態緩存發布的內容,對於以前生成的靜態頁面的內容很難修改,這樣用戶訪問舊頁面時,新的模板根本無法生效。
在動態緩存模式中,每個動態頁面只需要關心,而相關的其他頁面能自動更新,從而大大減少了設計相關頁面更新觸發器的需要。
軟道語錄
緩存
是把最常用的東西放在最容易取得的地方。
F. Okhttp解析(五)緩存的處理
大家好,之前我們講解了Okhttp網路數據請求相關的內容,這一節我們講講數據緩存的處理。本節按以下內容講解Okhttp緩存相關的內容。
緩存的使用場景很多,通過它可以將數據通過一定的規則存儲起來,再次請求數據的時候就可以快速從緩存中讀取了,緩存有以下優勢。
HTTP本身提供了一套緩存相關的機制。這套機制定義了相關的欄位和規則,用來客戶端和服務端進行緩存相關的協商,如響應的數據是否需要緩存,緩存有效期,緩存是否有效,伺服器端給出指示,而客戶端則根據服務端的指示做具體的緩存更新和讀取緩存工作。http緩存可以分為兩類:
強制緩存,在緩存數據未失效的情況下,可以直接使用緩存數據,有兩個欄位Expires和Cache-Control用於標明失效規則。
表示過期時間,由服務端返回。那麼下次請求數據時,判斷這個Expires過期時間是否已經過了,如果還沒有到過期時間,則使用緩存,如果過了過期時間,則重新請求伺服器的數據。Expires格式如下:
不過因為伺服器和客戶端的時間並不是同步的,用一個絕對時間作為過期的標記並不是很明智,所以HTTP1.1之後更多的是Cache-Control,它的控制更加靈活。
表示緩存的控制,有服務端返回。它有以下幾個取值:
默認情況下是private,也就是不能共享的。Cache-Control格式如下:
對比緩存,表示需要和服務端進行相關信息的對比,由伺服器決定是使用緩存還是最新內容,如果伺服器判定使用緩存,返回響應嗎304,判定使用最新內容,則返回響應碼200和最新數據。對比緩存的判定欄位有兩組:
ETag表示資源的一種標識信息,用於標識某個資源,由服務端返回,優先順序更高。格式如下:
然後客戶端再次請求時,加入欄位If-None-Match,格式如下:
服務端收到請求的該欄位時(之前的Etag值),和資源的唯一標識進行對比,如果相同,說明沒有改動,則返回狀態碼304,如果不同,說明資源被改過了,則返回狀態碼200和整個內容數據。
Last-Modified表示資源的最近修改時間,由服務端返回,優先順序更低。格式如下:
Last-Modified
由伺服器返回,表示響應的數據最近修改的時間。
If-Modified-Since
由客戶端請求,表示詢問伺服器這個時間是不是上次修改的時間。如果服務端該資源的修改時間小於等於If-Modified-Since指定的時間,說明資源沒有改動,返回響應狀態碼304,可以使用緩存。如果服務端該資源的修改時間大於If-Modified-Since指定的時間,說明資源又有改動了,則返回響應狀態碼200和最新數據給客戶端,客戶端使用響應返回的最新數據。
Last-Modified欄位的值(服務端返回的資源上次修改時間),常常被用於客戶端下次請求時的If-Modified-Since欄位中。
HTTP的緩存規則是優先考慮強制緩存,然後考慮對比緩存。
Okhttp緩存相關的類有如下:
要開啟使用Okhttp的緩存其實很簡單,只需要給OkHttpClient對象設置一個Cache對象即可,創建一個Cache時指定緩存保存的目錄和緩存最大的大小即可。
那麼下面我們來看看Okhttp緩存執行的大概流程
Okhttp的緩存流程分為讀取緩存和存儲緩存兩個過程,我們分別分析。
讀取使用緩存的流程從HttpEngine的sendRequest發送請求開始。
接下來我們分析
從Cache的get方法開始。它按以下步驟進行。
如果存在緩存的話,在指定的緩存目錄中,會有兩個文件「****.0」和「****.1」,分別存儲某個請求緩存的響應頭和響應體信息。(「****」是url的md5加密值)對應的ENTRY_METADATA響應頭和ENTRY_BODY響應體。緩存的讀取其實是由DiskLruCache來讀取的,DiskLruCache是支持Lru(最近最少訪問)規則的用於磁碟存儲的類,對應LruCache內存存儲。它在存儲的內容超過指定值之後,就會根據最近最少訪問的規則,把最近最少訪問的數據移除,以達到總大小不超過限制的目的。
接下來我們分析CacheStrategy緩存策略是怎麼判定的。
直接看CacheStrategy的get方法。緩存策略是由請求和緩存響應共同決定的。
接來下我們看看CacheControl類里有些什麼。
可以發現,它就是用於描述響應的緩存控制信息。
然後我們再看看Okhttp存儲緩存是怎麼進行的。
存儲緩存的流程從HttpEngine的readResponse發送請求開始的。
可以看到這里先通過maybeCache寫入了響應頭信息,再通過cacheWritingResponse寫入了響應體信息。我們再進去看Cache的put方法實現。
我們繼續看Cache的writeTo方法,可以看到是寫入一些響應頭信息。
到這里Okhttp緩存的讀取和存儲流程我們就清楚了。可以說,緩存的使用策略基本都是按照HTTP的緩存定義來實現的,所以對HTTP緩存相關欄位的理解是很重要的。然後關於DiskLruCache是如何管理緩存文件的,這個其實也很好理解,首先的原則就是按照LRU這種最近最少使用刪除的原則,當總的大小超過限定大小後,刪除最近最少使用的緩存文件,它的LRU演算法是使用LinkedHashMap進行維護的,這樣來保證,保留的緩存文件都是更常使用的。具體實現大家可以分析DiskLruCache和LinkedHashMap的實現原理。