ThinkPHP緩存源碼深度解析

緩存在項(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)用場景并不說是框架的緩存,一般在使用緩存的層面是不太使用框架的緩存的。

常用的為redismemcachenosql

但是今天主要討論的是框架中緩存,所以千萬不要認(rèn)為框架的緩存是無所不能的,還是要看項(xiàng)目的實(shí)際情況。

一、緩存cache設(shè)置的執(zhí)行流程以及源碼解析

首先需要實(shí)現(xiàn)以下的案例,并且引入cache類

ThinkPHP緩存源碼深度解析
演示案例

cache怎么運(yùn)行的?

就代碼Cache::set這個(gè)現(xiàn)在知道是怎么運(yùn)行的嗎?如果不知道咔咔帶你在來深入的學(xué)習(xí)一次。

我們都知道框架的入口文件是index.php,在入口文件中引入了一個(gè)文件為base.php。

ThinkPHP緩存源碼深度解析
入口文件

來到base.php這個(gè)文件里邊可以看到關(guān)于注冊類庫別名,至于是怎么注冊的,這個(gè)在框架執(zhí)行流程的那一節(jié)中有過深度的講解,可以回過頭在去了解一下。

ThinkPHP緩存源碼深度解析
注冊類庫別名

所以說代碼將會執(zhí)行到框架核心的facade這個(gè)類里邊,在這個(gè)類里邊存在一個(gè)方法__callStatic,當(dāng)調(diào)用不存在的靜態(tài)方法時(shí)此方法會進(jìn)行執(zhí)行。

ThinkPHP緩存源碼深度解析
門面的核心類

那么怎么來做這個(gè)驗(yàn)證呢!不能咔咔這樣說就是這樣的對吧!

那么代碼將會接著來到創(chuàng)建Facade實(shí)例這個(gè)方法,我們做的測試就是將這個(gè)class打印出來得到的值都有什么。

ThinkPHP緩存源碼深度解析
創(chuàng)建Facade實(shí)例

暫時(shí)先不管這個(gè)cache執(zhí)行了幾次,是可以明顯的看到打印結(jié)果是存在這個(gè)值的,所以說從另一個(gè)笨拙的方面驗(yàn)證了咔咔的說辭。

ThinkPHP緩存源碼深度解析
打印的測試值

這里有一個(gè)特別小的細(xì)節(jié)我想大家應(yīng)該需要了解一下,那就是關(guān)于static的使用

關(guān)于static的小技巧

首先可以看到cache類是繼承這Facade門面類

ThinkPHP緩存源碼深度解析
類的繼承

然后static是在門面類中做的使用,那么最終返回的類就是繼承門面類的那個(gè)類也就是cache類

ThinkPHP緩存源碼深度解析
門面類演示

總結(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ā)的方法。

ThinkPHP緩存源碼深度解析
緩存的類

自動初始化緩存

根據(jù)執(zhí)行流程我們將會看到init這個(gè)方法自動初始化緩存(這里需要注意,第一次并不是在這里進(jìn)行執(zhí)行的,而是make方法,當(dāng)make方法執(zhí)行完后會把值存放在handler這個(gè)屬性,第二次通過call方法進(jìn)來之后就直接返回了,而不會在進(jìn)行一次執(zhí)行,這里一定要注意)

ThinkPHP緩存源碼深度解析
自動初始化緩存

在這里我們進(jìn)行打印一次$options這個(gè)的值。

ThinkPHP緩存源碼深度解析
打印結(jié)果

探討一下為什么$options這個(gè)參數(shù)會有值

這里就是關(guān)于容器方面的知識了,來咔咔帶你看一下。

ThinkPHP緩存源碼深度解析
make方法

當(dāng)在創(chuàng)建Cache時(shí)創(chuàng)建Facade實(shí)例,在這個(gè)過程中注意咔咔下圖圈起來的部位,執(zhí)行了一個(gè)見了八百次的make方法了。

ThinkPHP緩存源碼深度解析
創(chuàng)建Facade實(shí)例

來到make方法只需要看咔咔圈起來的地方即可

ThinkPHP緩存源碼深度解析
make方法第二次執(zhí)行的位置

然后在進(jìn)入到invokeClass方法,這個(gè)方法是調(diào)用反射執(zhí)行類的實(shí)例化 支持依賴注入。

在這個(gè)方法中通過反射執(zhí)行了Cache中的make方法。

ThinkPHP緩存源碼深度解析
調(diào)用反射執(zhí)行類的實(shí)例化 支持依賴注入

所以就會執(zhí)行Cache類中的make方法,這個(gè)方法就會進(jìn)行實(shí)例化本類,并且執(zhí)行構(gòu)造函數(shù),接下來看一下。

ThinkPHP緩存源碼深度解析
緩存類的make方法

來到構(gòu)造函數(shù)中你會看到從make方法獲取到的cache配置文件的配置項(xiàng)傳進(jìn)了init方法,也就是自動初始化緩存的部分。

ThinkPHP緩存源碼深度解析
構(gòu)造函數(shù)

所以從這里看到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里邊。

ThinkPHP緩存源碼深度解析
連接緩存

最終代碼會返回給cache中的__call方法,類為Object(thinkcachedriverFile)方法為set

ThinkPHP緩存源碼深度解析
調(diào)用不存在的方法時(shí)會進(jìn)行執(zhí)行

于是執(zhí)行流程會來到下圖位置寫入緩存

ThinkPHP緩存源碼深度解析
寫入緩存

獲取文件名

在這個(gè)方法中主要需要理解的一件事情就是在緩存中是如何進(jìn)行獲取具體的文件名然后進(jìn)行存儲數(shù)據(jù)的。

這個(gè)name值就是咱們需要設(shè)置的值,wechat。

ThinkPHP緩存源碼深度解析
取得變量的存儲文件名

然后來到getCacheKey取得變量的存儲文件名。

在這個(gè)方法中第一步就是通過hash的方式進(jìn)行類型和緩存值加密,這個(gè)options是在本類聲明好了的,這里一定要明確。

因?yàn)樵诳蚣苤写罅康氖褂昧薿ptions這個(gè)變量千萬不要搞混淆了。

在這個(gè)方法中需要明白的就是這個(gè)文件名是怎么確定的。

ThinkPHP緩存源碼深度解析
獲取文件名

還是要來到本類的開始位置查看一下這個(gè)options的值,在這個(gè)類中可以看到上圖中使用的加密類型為hash_type就是md5

ThinkPHP緩存源碼深度解析
關(guān)于緩存到文件的值

然后來到構(gòu)造函數(shù)中可以看到關(guān)于path的設(shè)置

ThinkPHP緩存源碼深度解析
獲取緩存文件的路徑

在上圖中可以看到Container::get(‘app’)這行代碼,這行代碼就是使用的容器執(zhí)行的也是make方法,關(guān)于這個(gè)make方法在容器中起到的作用是十分大的,所以需要好好理解。

然后有一個(gè)小細(xì)節(jié)不知道大家有沒有看到,那就是在下方有一個(gè)init方法,我們一起去看看這個(gè)方法是干什么的。

來到這個(gè)方法后你會發(fā)現(xiàn)這里是直接按照獲取到的緩存文件的路徑進(jìn)行創(chuàng)建文件。

ThinkPHP緩存源碼深度解析
初始化檢查

這時(shí)可以查看一下創(chuàng)建的文件,可以看到文件已經(jīng)創(chuàng)建好了。

ThinkPHP緩存源碼深度解析
緩存文件的路徑

最后通過file_put_contents函數(shù)將數(shù)據(jù)存放至剛剛獲取到的緩存文件存放位置

ThinkPHP緩存源碼深度解析
在緩存文件中寫入數(shù)據(jù)

數(shù)據(jù)庫存儲形式就是下圖

ThinkPHP緩存源碼深度解析
數(shù)據(jù)存儲形式

直到這里關(guān)于框架緩存設(shè)置就結(jié)束了,其實(shí)流程并不難,在這個(gè)案例中咔咔使用的文件形式的,至于redis還是其它都是一樣的。

二、緩存cache獲取的執(zhí)行流程以及源碼解析

既然學(xué)習(xí)了緩存設(shè)置的源碼解析,那么也應(yīng)該來簡單的了解一下緩存獲取的源碼解析。

同樣演示案例還是之前的那個(gè),只不過是把set換為get即可

ThinkPHP緩存源碼深度解析
演示案例

跟設(shè)置緩存的流程是一樣的,首先會來到門面類中創(chuàng)建緩存的對應(yīng)實(shí)例

ThinkPHP緩存源碼深度解析
門面類創(chuàng)建緩存實(shí)例

門面類創(chuàng)建了緩存類的時(shí)候之后就會來到cache類這個(gè)文件thinkphp/library/think/Cache.php

在這個(gè)文件中可以看到還是使用了__call方法,這個(gè)方法就是調(diào)用不存在的方法會執(zhí)行。

ThinkPHP緩存源碼深度解析
緩存的文件

接著會來到這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í)行。

ThinkPHP緩存源碼深度解析
自動初始化緩存

所以說在cache這個(gè)類中return call_user_func_array([$this->init(), $method], $args);這塊代碼會去執(zhí)行thinkphp/library/think/cache/driver/File.php這個(gè)類的get方法

ThinkPHP緩存源碼深度解析
讀取緩存

在這個(gè)類中你可以看到一個(gè)在設(shè)置緩存值時(shí)花了好久解析的一個(gè)方法getCacheKey

在這個(gè)方法中主要就是使用了sbustr來進(jìn)行了加密值的節(jié),前倆個(gè)值為目錄,其余字符為文件名。

然后將文件名給返回出去。

ThinkPHP緩存源碼深度解析
取得變量的存儲文件名

然后就使用file_get_contents來將文件的內(nèi)容獲取出來

ThinkPHP緩存源碼深度解析
讀取文件內(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í)行刪除。

ThinkPHP緩存源碼深度解析
過期杉樹緩存文件

截止到這里關(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格式;

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊12 分享