“
緩存在項(xiàng)目的運(yùn)行了一段時(shí)間都會使用的一個(gè)功能,本文將會對框架中的緩存做一個(gè)深度的解析
”
前言
在項(xiàng)目中緩存是必不可少的一項(xiàng)功能,當(dāng)用戶量大的時(shí)候是必須上緩存的,如何都直接查數(shù)據(jù)庫那么對于用戶體驗(yàn)來說就太差了。
那么什么情況下應(yīng)該使用緩存呢!
-
熱點(diǎn)事件,例如微博熱搜 -
不經(jīng)常更新的數(shù)據(jù),例如配置項(xiàng) -
博客平臺的排行榜單 -
社交平臺的關(guān)注列表粉絲列表等等
以上說的這些應(yīng)用場景并不說是框架的緩存,一般在使用緩存的層面是不太使用框架的緩存的。
但是今天主要討論的是框架中緩存,所以千萬不要認(rèn)為框架的緩存是無所不能的,還是要看項(xiàng)目的實(shí)際情況。
一、緩存cache設(shè)置的執(zhí)行流程以及源碼解析
首先需要實(shí)現(xiàn)以下的案例,并且引入cache類
cache怎么運(yùn)行的?
就代碼Cache::set這個(gè)現(xiàn)在知道是怎么運(yùn)行的嗎?如果不知道咔咔帶你在來深入的學(xué)習(xí)一次。
我們都知道框架的入口文件是index.php,在入口文件中引入了一個(gè)文件為base.php。
來到base.php這個(gè)文件里邊可以看到關(guān)于注冊類庫別名,至于是怎么注冊的,這個(gè)在框架執(zhí)行流程的那一節(jié)中有過深度的講解,可以回過頭在去了解一下。
所以說代碼將會執(zhí)行到框架核心的facade這個(gè)類里邊,在這個(gè)類里邊存在一個(gè)方法__callStatic,當(dāng)調(diào)用不存在的靜態(tài)方法時(shí)此方法會進(jìn)行執(zhí)行。
那么怎么來做這個(gè)驗(yàn)證呢!不能咔咔這樣說就是這樣的對吧!
那么代碼將會接著來到創(chuàng)建Facade實(shí)例這個(gè)方法,我們做的測試就是將這個(gè)class打印出來得到的值都有什么。
暫時(shí)先不管這個(gè)cache執(zhí)行了幾次,是可以明顯的看到打印結(jié)果是存在這個(gè)值的,所以說從另一個(gè)笨拙的方面驗(yàn)證了咔咔的說辭。
這里有一個(gè)特別小的細(xì)節(jié)我想大家應(yīng)該需要了解一下,那就是關(guān)于static的使用
關(guān)于static的小技巧
首先可以看到cache類是繼承這Facade門面類
然后static是在門面類中做的使用,那么最終返回的類就是繼承門面類的那個(gè)類也就是cache類
總結(jié)為一句話就為
static 如果有被繼承的話 默認(rèn)調(diào)用子類 ,否則調(diào)用的是自身
所以說下邊接著的static::getFacadeClass()這里也是執(zhí)行的子類中的方法。
好了,進(jìn)入了一段小插曲,接下來會到正題。
所以說代碼將會執(zhí)行到thinkphp/library/think/Cache.php這個(gè)文件,也就是核心類庫的位置。
在這個(gè)方法你是找不到set方法的,所以代碼將會執(zhí)行到__call方法,這個(gè)方法當(dāng)調(diào)用不存在的方法時(shí)則會觸發(fā)的方法。
自動初始化緩存
根據(jù)執(zhí)行流程我們將會看到init這個(gè)方法自動初始化緩存(這里需要注意,第一次并不是在這里進(jìn)行執(zhí)行的,而是make方法,當(dāng)make方法執(zhí)行完后會把值存放在handler這個(gè)屬性,第二次通過call方法進(jìn)來之后就直接返回了,而不會在進(jìn)行一次執(zhí)行,這里一定要注意)
在這里我們進(jìn)行打印一次$options這個(gè)的值。
探討一下為什么$options這個(gè)參數(shù)會有值
這里就是關(guān)于容器方面的知識了,來咔咔帶你看一下。
當(dāng)在創(chuàng)建Cache時(shí)創(chuàng)建Facade實(shí)例,在這個(gè)過程中注意咔咔下圖圈起來的部位,執(zhí)行了一個(gè)見了八百次的make方法了。
來到make方法只需要看咔咔圈起來的地方即可
然后在進(jìn)入到invokeClass方法,這個(gè)方法是調(diào)用反射執(zhí)行類的實(shí)例化 支持依賴注入。
在這個(gè)方法中通過反射執(zhí)行了Cache中的make方法。
所以就會執(zhí)行Cache類中的make方法,這個(gè)方法就會進(jìn)行實(shí)例化本類,并且執(zhí)行構(gòu)造函數(shù),接下來看一下。
來到構(gòu)造函數(shù)中你會看到從make方法獲取到的cache配置文件的配置項(xiàng)傳進(jìn)了init方法,也就是自動初始化緩存的部分。
所以從這里看到init方法自動初始化緩存第一次執(zhí)行是在容器實(shí)例化的時(shí)候執(zhí)行的所以$options才會存在值。
接下來將順著這個(gè)流程進(jìn)行連接緩存也就是代碼$this->handler = $this->connect($options);這塊的內(nèi)容。
這個(gè)方法就很簡單了,就是使用了之前一直講解的工廠模式實(shí)現(xiàn)的加載不同類型的緩存方式。
然后會把返回的對象存放在以$optionsmd5為下標(biāo)的緩存實(shí)例屬性$instance里邊。
最終代碼會返回給cache中的__call方法,類為Object(thinkcachedriverFile)方法為set
于是執(zhí)行流程會來到下圖位置寫入緩存
獲取文件名
在這個(gè)方法中主要需要理解的一件事情就是在緩存中是如何進(jìn)行獲取具體的文件名然后進(jìn)行存儲數(shù)據(jù)的。
這個(gè)name值就是咱們需要設(shè)置的值,wechat。
然后來到getCacheKey取得變量的存儲文件名。
在這個(gè)方法中第一步就是通過hash的方式進(jìn)行類型和緩存值加密,這個(gè)options是在本類聲明好了的,這里一定要明確。
因?yàn)樵诳蚣苤写罅康氖褂昧薿ptions這個(gè)變量千萬不要搞混淆了。
在這個(gè)方法中需要明白的就是這個(gè)文件名是怎么確定的。
還是要來到本類的開始位置查看一下這個(gè)options的值,在這個(gè)類中可以看到上圖中使用的加密類型為hash_type就是md5
然后來到構(gòu)造函數(shù)中可以看到關(guān)于path的設(shè)置
在上圖中可以看到Container::get(‘app’)這行代碼,這行代碼就是使用的容器執(zhí)行的也是make方法,關(guān)于這個(gè)make方法在容器中起到的作用是十分大的,所以需要好好理解。
然后有一個(gè)小細(xì)節(jié)不知道大家有沒有看到,那就是在下方有一個(gè)init方法,我們一起去看看這個(gè)方法是干什么的。
來到這個(gè)方法后你會發(fā)現(xiàn)這里是直接按照獲取到的緩存文件的路徑進(jìn)行創(chuàng)建文件。
這時(shí)可以查看一下創(chuàng)建的文件,可以看到文件已經(jīng)創(chuàng)建好了。
最后通過file_put_contents函數(shù)將數(shù)據(jù)存放至剛剛獲取到的緩存文件存放位置
數(shù)據(jù)庫存儲形式就是下圖
直到這里關(guān)于框架緩存設(shè)置就結(jié)束了,其實(shí)流程并不難,在這個(gè)案例中咔咔使用的文件形式的,至于redis還是其它都是一樣的。
二、緩存cache獲取的執(zhí)行流程以及源碼解析
既然學(xué)習(xí)了緩存設(shè)置的源碼解析,那么也應(yīng)該來簡單的了解一下緩存獲取的源碼解析。
同樣演示案例還是之前的那個(gè),只不過是把set換為get即可
跟設(shè)置緩存的流程是一樣的,首先會來到門面類中創(chuàng)建緩存的對應(yīng)實(shí)例
門面類創(chuàng)建了緩存類的時(shí)候之后就會來到cache類這個(gè)文件thinkphp/library/think/Cache.php
在這個(gè)文件中可以看到還是使用了__call方法,這個(gè)方法就是調(diào)用不存在的方法會執(zhí)行。
接著會來到這init方法,這個(gè)方法在設(shè)置緩存值的時(shí)候已經(jīng)進(jìn)行深入講解了。
根據(jù)執(zhí)行流程我們將會看到init這個(gè)方法自動初始化緩存(這里需要注意,第一次并不是在這里進(jìn)行執(zhí)行的,而是make方法,當(dāng)make方法執(zhí)行完后會把值存放在handler這個(gè)屬性,第二次通過call方法進(jìn)來之后就直接返回了,而不會在進(jìn)行一次執(zhí)行,這里一定要注意)
這里為什么先會執(zhí)行cache的make方法,是因?yàn)樵谌萜髦袆?chuàng)建cache類實(shí)例的時(shí)候會在make方法中判斷類中是否存在make方法,如果存在就會先進(jìn)行執(zhí)行。
所以說在cache這個(gè)類中return call_user_func_array([$this->init(), $method], $args);這塊代碼會去執(zhí)行thinkphp/library/think/cache/driver/File.php這個(gè)類的get方法
在這個(gè)類中你可以看到一個(gè)在設(shè)置緩存值時(shí)花了好久解析的一個(gè)方法getCacheKey
在這個(gè)方法中主要就是使用了sbustr來進(jìn)行了加密值的節(jié),前倆個(gè)值為目錄,其余字符為文件名。
然后將文件名給返回出去。
然后就使用file_get_contents來將文件的內(nèi)容獲取出來
然后可以接著往下看,在這里有一個(gè)過期刪除緩存文件。
框架中的過期策略是當(dāng)你設(shè)置了過期時(shí)間時(shí),緩存過期后不會直接刪除而是當(dāng)你再一次訪問之后才會進(jìn)行刪除。
這種策略就是redis中的惰性刪除,當(dāng)我們使用惰性刪除時(shí),數(shù)據(jù)到期了也不會自動刪除,那么他的刪除方式是,在下一次在獲取這個(gè)key值時(shí),會做一個(gè)判斷,判斷這個(gè)key是否過期,如果過期了在執(zhí)行刪除。
截止到這里關(guān)于緩存獲取的執(zhí)行流程已經(jīng)源碼解析就完成了,其實(shí)大多數(shù)內(nèi)容在獲取的時(shí)候就已經(jīng)解析完了。
那為什么還是要在聊一下獲取緩存數(shù)據(jù),那是因?yàn)樵谶@里還需要給大家在解釋一些東西。
三、數(shù)據(jù)壓縮
在設(shè)置緩存的時(shí)候?qū)⒕彺娴闹祵懭胛募r(shí)有過一個(gè)函數(shù)gzcompress
然而在獲取緩存值的時(shí)候從文件將數(shù)據(jù)讀出時(shí)又遇到的一個(gè)函數(shù)gzuncompress
其實(shí)從設(shè)置緩存和獲取緩存的這倆個(gè)功能中就可以看出,設(shè)置的為壓縮數(shù)據(jù),獲取的解壓數(shù)據(jù)。
在PHP中關(guān)于壓縮函數(shù)還有其它倆個(gè)分別為gzdeflate、gzencode,同樣的解壓函數(shù)也是對應(yīng)的gzinflate gzdecode
這幾個(gè)函數(shù)雖說都是壓縮函數(shù),但是底層實(shí)現(xiàn)是不一樣的。
gzcompress使用的是ZLIB格式;
gzdeflate使用的是純粹的DEFLATE格式;
gzencode使用的是GZIP格式;