Rumah >rangka kerja php >ThinkPHP >ThinkPHP缓存源码深度解析

ThinkPHP缓存源码深度解析

咔咔
咔咔asal
2021-01-18 22:46:261899semak imbas

缓存在项目的运行了一段时间都会使用的一个功能,本文将会对框架中的缓存做一个深度的解析

前言

在项目中缓存是必不可少的一项功能,当用户量大的时候是必须上缓存的,如何都直接查数据库那么对于用户体验来说就太差了。

那么什么情况下应该使用缓存呢!

  • 热点事件,例如微博热搜
  • 不经常更新的数据,例如配置项
  • 博客平台的排行榜单
  • 社交平台的关注列表粉丝列表等等

以上说的这些应用场景并不说是框架的缓存,一般在使用缓存的层面是不太使用框架的缓存的。

常用的为redis,memcache等NoSQL。

但是今天主要讨论的是框架中缓存,所以千万不要认为框架的缓存是无所不能的,还是要看项目的实际情况。

一、缓存cache设置的执行流程以及源码解析

首先需要实现以下的案例,并且引入cache类

演示案例
演示案例

cache怎么运行的?

就代码Cache::set这个现在知道是怎么运行的吗?如果不知道咔咔带你在来深入的学习一次。

我们都知道框架的入口文件是index.php,在入口文件中引入了一个文件为base.php。

入口文件
入口文件

来到base.php这个文件里边可以看到关于注册类库别名,至于是怎么注册的,这个在框架执行流程的那一节中有过深度的讲解,可以回过头在去了解一下。

注册类库别名
注册类库别名

所以说代码将会执行到框架核心的facade这个类里边,在这个类里边存在一个方法__callStatic,当调用不存在的静态方法时此方法会进行执行。

门面的核心类
门面的核心类

那么怎么来做这个验证呢!不能咔咔这样说就是这样的对吧!

那么代码将会接着来到创建Facade实例这个方法,我们做的测试就是将这个class打印出来得到的值都有什么。

创建Facade实例
创建Facade实例

暂时先不管这个cache执行了几次,是可以明显的看到打印结果是存在这个值的,所以说从另一个笨拙的方面验证了咔咔的说辞。

打印的测试值
打印的测试值

这里有一个特别小的细节我想大家应该需要了解一下,那就是关于static的使用

关于static的小技巧

首先可以看到cache类是继承这Facade门面类

类的继承
类的继承

然后static是在门面类中做的使用,那么最终返回的类就是继承门面类的那个类也就是cache类

门面类演示
门面类演示

总结为一句话就为

static 如果有被继承的话 默认调用子类 ,否则调用的是自身

所以说下边接着的static::getFacadeClass()这里也是执行的子类中的方法。

好了,进入了一段小插曲,接下来会到正题。

所以说代码将会执行到thinkphp/library/think/Cache.php这个文件,也就是核心类库的位置。

在这个方法你是找不到set方法的,所以代码将会执行到__call方法,这个方法当调用不存在的方法时则会触发的方法。

缓存的类
缓存的类

自动初始化缓存

根据执行流程我们将会看到init这个方法自动初始化缓存(这里需要注意,第一次并不是在这里进行执行的,而是make方法,当make方法执行完后会把值存放在handler这个属性,第二次通过call方法进来之后就直接返回了,而不会在进行一次执行,这里一定要注意)

自动初始化缓存
自动初始化缓存

在这里我们进行打印一次$options这个的值。

打印结果
打印结果

探讨一下为什么$options这个参数会有值

这里就是关于容器方面的知识了,来咔咔带你看一下。

make方法
make方法

当在创建Cache时创建Facade实例,在这个过程中注意咔咔下图圈起来的部位,执行了一个见了八百次的make方法了。

创建Facade实例
创建Facade实例

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

make方法第二次执行的位置
make方法第二次执行的位置

然后在进入到invokeClass方法,这个方法是调用反射执行类的实例化 支持依赖注入。

在这个方法中通过反射执行了Cache中的make方法。

调用反射执行类的实例化 支持依赖注入
调用反射执行类的实例化 支持依赖注入

所以就会执行Cache类中的make方法,这个方法就会进行实例化本类,并且执行构造函数,接下来看一下。

缓存类的make方法
缓存类的make方法

来到构造函数中你会看到从make方法获取到的cache配置文件的配置项传进了init方法,也就是自动初始化缓存的部分。

构造函数
构造函数

所以从这里看到init方法自动初始化缓存第一次执行是在容器实例化的时候执行的所以$options才会存在值。

接下来将顺着这个流程进行连接缓存也就是代码$this->handler = $this->connect($options);这块的内容。

这个方法就很简单了,就是使用了之前一直讲解的工厂模式实现的加载不同类型的缓存方式。

然后会把返回的对象存放在以$optionsmd5为下标的缓存实例属性$instance里边。

连接缓存
连接缓存

最终代码会返回给cache中的__call方法,类为object(think\cache\driver\File)方法为set

调用不存在的方法时会进行执行
调用不存在的方法时会进行执行

于是执行流程会来到下图位置写入缓存

写入缓存
写入缓存

获取文件名

在这个方法中主要需要理解的一件事情就是在缓存中是如何进行获取具体的文件名然后进行存储数据的。

这个name值就是咱们需要设置的值,wechat。

取得变量的存储文件名
取得变量的存储文件名

然后来到getCacheKey取得变量的存储文件名。

在这个方法中第一步就是通过hash的方式进行类型和缓存值加密,这个options是在本类声明好了的,这里一定要明确。

因为在框架中大量的使用了options这个变量千万不要搞混淆了。

在这个方法中需要明白的就是这个文件名是怎么确定的。

获取文件名
获取文件名

还是要来到本类的开始位置查看一下这个options的值,在这个类中可以看到上图中使用的加密类型为hash_type就是md5

关于缓存到文件的值
关于缓存到文件的值

然后来到构造函数中可以看到关于path的设置

获取缓存文件的路径
获取缓存文件的路径

在上图中可以看到Container::get('app')这行代码,这行代码就是使用的容器执行的也是make方法,关于这个make方法在容器中起到的作用是十分大的,所以需要好好理解。

然后有一个小细节不知道大家有没有看到,那就是在下方有一个init方法,我们一起去看看这个方法是干什么的。

来到这个方法后你会发现这里是直接按照获取到的缓存文件的路径进行创建文件。

初始化检查
初始化检查

这时可以查看一下创建的文件,可以看到文件已经创建好了。

缓存文件的路径
缓存文件的路径

最后通过file_put_contents函数将数据存放至刚刚获取到的缓存文件存放位置

在缓存文件中写入数据
在缓存文件中写入数据

数据库存储形式就是下图

数据存储形式
数据存储形式

直到这里关于框架缓存设置就结束了,其实流程并不难,在这个案例中咔咔使用的文件形式的,至于redis还是其它都是一样的。

二、缓存cache获取的执行流程以及源码解析

既然学习了缓存设置的源码解析,那么也应该来简单的了解一下缓存获取的源码解析。

同样演示案例还是之前的那个,只不过是把set换为get即可

演示案例
演示案例

跟设置缓存的流程是一样的,首先会来到门面类中创建缓存的对应实例

门面类创建缓存实例
门面类创建缓存实例

门面类创建了缓存类的时候之后就会来到cache类这个文件thinkphp/library/think/Cache.php

在这个文件中可以看到还是使用了__call方法,这个方法就是调用不存在的方法会执行。

缓存的文件
缓存的文件

接着会来到这init方法,这个方法在设置缓存值的时候已经进行深入讲解了。

根据执行流程我们将会看到init这个方法自动初始化缓存(这里需要注意,第一次并不是在这里进行执行的,而是make方法,当make方法执行完后会把值存放在handler这个属性,第二次通过call方法进来之后就直接返回了,而不会在进行一次执行,这里一定要注意)

这里为什么先会执行cache的make方法,是因为在容器中创建cache类实例的时候会在make方法中判断类中是否存在make方法,如果存在就会先进行执行。

自动初始化缓存
自动初始化缓存

所以说在cache这个类中return call_user_func_array([$this->init(), $method], $args);这块代码会去执行thinkphp/library/think/cache/driver/File.php这个类的get方法

读取缓存
读取缓存

在这个类中你可以看到一个在设置缓存值时花了好久解析的一个方法getCacheKey

在这个方法中主要就是使用了sbustr来进行了加密值的节,前俩个值为目录,其余字符为文件名。

然后将文件名给返回出去。

取得变量的存储文件名
取得变量的存储文件名

然后就使用file_get_contents来将文件的内容获取出来

读取文件内容
读取文件内容

然后可以接着往下看,在这里有一个过期删除缓存文件。

框架中的过期策略是当你设置了过期时间时,缓存过期后不会直接删除而是当你再一次访问之后才会进行删除。

这种策略就是redis中的惰性删除,当我们使用惰性删除时,数据到期了也不会自动删除,那么他的删除方式是,在下一次在获取这个key值时,会做一个判断,判断这个key是否过期,如果过期了在执行删除。

过期杉树缓存文件
过期杉树缓存文件

截止到这里关于缓存获取的执行流程已经源码解析就完成了,其实大多数内容在获取的时候就已经解析完了。

那为什么还是要在聊一下获取缓存数据,那是因为在这里还需要给大家在解释一些东西。

三、数据压缩

在设置缓存的时候将缓存的值写入文件时有过一个函数gzcompress

然而在获取缓存值的时候从文件将数据读出时又遇到的一个函数gzuncompress

其实从设置缓存和获取缓存的这俩个功能中就可以看出,设置的为压缩数据,获取的解压数据。

在PHP中关于压缩函数还有其它俩个分别为gzdeflate、gzencode,同样的解压函数也是对应的gzinflate gzdecode

这几个函数虽说都是压缩函数,但是底层实现是不一样的。

gzcompress使用的是ZLIB格式;

gzdeflate使用的是纯粹的DEFLATE格式;

gzencode使用的是GZIP格式;

以上就是关于数据压缩和解压的一些知识,了解即可。

四、总结

在这一节中咔咔带大家领略了框架对于缓存的处理结果。

其实咔咔之前测试过关于框架自带的缓存响应时间应该会缩短三分之一,当然这个也是根据数据量的大小有区别的。

至此关于PHP框架ThinkPHP的源码解读到这里就结束了,后期如果有时间将会对其中一些没有提到的内容在进行解读。

最后说一句阅读源码是真的累。

坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在诺大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。

Atas ialah kandungan terperinci ThinkPHP缓存源码深度解析. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn