set cache php,TP5之Cache的原理及使用

在当今大流量的互联网之中,Cache的重要性不言而喻。ThinkPhp5作为国内主流框架,提供了强大的Cache功能。让我们跟随本文,来剖析TP5 Cache的原理及使用。

为什么需要Cache(缓存)?

假设现在有一个小说网,有非常多的读者,有一篇新的章节更新了,那么可能一分钟内有几万几十万的访问量.

如果没有缓存,同样的内容就要去数据库重复查询,那可能网站一下就挂掉了.

追求性能的web站点应该充分利用缓存,常见的缓存类型有File,Memcache,Redis等,这里就不说他们的区别了

今天我们分析下TP5 Cache的内部实现原理.

首先看官方文档如何使用缓存的.

817b11c1166febb912cb5b45faeab9f9.png

如上图,调用Cache类的的静态方法set就可以直接使用了,我们查看Cache类文件 在application/thinkphp/library/think目录下protected static $instance = [];

public static $readTimes = 0;

public static $writeTimes = 0;

/**

* 操作句柄

* @var object

* @access protected */

protected static $handler; /**

* 写入缓存

* @access public

* @param string $name 缓存标识

* @param mixed $value 存储数据

* @param int|null $expire 有效时间 0为永久

* @return boolean */

public static function set($name, $value, $expire = null)

{

self::$writeTimes++;

return self::init()->set($name, $value, $expire);

}

看到原来set方法是这样的, 其中writeTimes 是Cache类的静态变量,主要记录缓存的读取次数,这不是重点.

注意到了吗,有个静态变量命名为 $instance, 上次说过这样命名大概率就是 单例模式了.

set方法的重点是init方法

我们再看init方法public static function init(array $options = [])

{ if (is_null(self::$handler)) { // 自动初始化缓存

if (!empty($options)) {

$connect = self::connect($options);

} elseif ('complex' == Config::get('cache.type')) {

$connect = self::connect(Config::get('cache.default'));

} else {

$connect = self::connect(Config::get('cache'));

}

self::$handler = $connect;

} return self::$handler;

}

handler就是操作的句柄(巨饼:-) ), 这里一看,果然是单例模式了,如果句柄为空才去初始化对象,不然直接返回.句柄

同样,这里重点是connect函数, 传入的参数是 配置信息

同样,我们查看connect方法/**

* 连接缓存

* @access public

* @param array $options 配置数组

* @param bool|string $name 缓存连接标识 true 强制重新连接

* @return Driver

*/

public static function connect(array $options = [], $name = false)

{

$type = !empty($options['type']) ? $options['type'] : 'File';

if (false === $name) {

$name = md5(serialize($options));

}

if (true === $name || !isset(self::$instance[$name])) {

$class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type);

// 记录初始化信息

App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');

if (true === $name) {

return new $class($options);

} else {

self::$instance[$name] = new $class($options);

}

}

return self::$instance[$name];

}

self::$instance[$name] = new $class($options); 这一句里,我们就可以知道句柄的真实身份拉,

$class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type);

这一句的意思是class的名字由type决定, 如果type没有包含反斜线, 则class = \think\cache\driver\.ucwords($type)

thinkPhp 是把think作为核心目录的别名的,所以他真实路径就是 \thinkphp\libray\\think\driver\.ucwords($type)

根据自动加载的尿性,自然是去该文件夹下加载对应的对象

(额外提一句,这利用的是PHP动态变量的一个特性,其实就和工厂模式一个原理,运行中动态决定实例化的对象)

type是什么呢? type就是函数传入的参数,也就是配置信息,我们看下配置信息

ab88685e501ce9f19fd693c97fc20220.pngtype就是驱动方式,如果我们type填写的是File,那么就使用文件驱动,实例化的是 \think\cache\driver\File.class

我们看下 \think\cache\driver文件下有什么文件,那就知道thinkphp为我们提供了多少种缓存驱动了

d084afccfc72cf9b0cbce9ac5721362b.png

原来有这么多!

点进去

每个文件,我们可以发现一个共同点, 每个类都是继承了 抽象类 Driver

Driver决定了 每一个Cache驱动应该是什么样子的,他们的方法基本是一样的,而实现方式因每个驱动不同而异

其实这就是 适配器模式,如果是我们自己写,当然不会写那么多拉,不过TP5是为了造福广大PHP开发者,所以编写了那么多不同的驱动供我们使用.

我们重点看Redis吧, 如果要去实验,记得把 config中的 Cache.type更改为 redis

Redis类的方法很少,先看看构造函数public function __construct($options = [])

{

if (!extension_loaded('redis')) {

throw new \BadFunctionCallException('not support: redis');

}

if (!empty($options)) {

$this->options = array_merge($this->options, $options);

}

$func = $this->options['persistent'] ? 'pconnect' : 'connect';

$this->handler = new \Redis;

$this->handler->$func($this->options['host'], $this->options['port'], $this->options['timeout']);

if ('' != $this->options['password']) {

$this->handler->auth($this->options['password']);

}

if (0 != $this->options['select']) {

$this->handler->select($this->options['select']);

}

}

可见TP5的 redis驱动 是基于phpredis的阿, handler 就是实例化的phpredis类, 因此选了哪个驱动,Cache的类自然就是哪些驱动.

所以说如果要使用 TP5的 redis,必须要先安装phpredis扩展.

这里就顺便解析下 redis重写的 set方法/**

* 写入缓存

* @access public

* @param string $name 缓存变量名

* @param mixed $value 存储数据

* @param integer $expire 有效时间(秒)

* @return boolean

*/

public function set($name, $value, $expire = null)

{

if (is_null($expire)) {

$expire = $this->options['expire'];

}

if ($this->tag && !$this->has($name)) {

$first = true;

}

$key = $this->getCacheKey($name);

//对数组/对象数据进行缓存处理,保证数据完整性 byron sampson

$value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;

if (is_int($expire) && $expire) {

$result = $this->handler->setex($key, $expire, $value);

} else {

$result = $this->handler->set($key, $value);

}

isset($first) && $this->setTagItem($key);

return $result;

}

原本的phpredis set方法 只能是 普通的键值对, 而重写的set方法现在可以是 键,数组啦,这是非常有用的方法

可以看到实现的 原理是把 数组或者对象 序列化为json, 取值的时候则反序列化成为数组.

到这里我们就基本分析完了一个驱动是如何实现的,首先必须 继承Driver类,实现Driver规定的方法,然后将handler交给Cache类去使用

我们回到Cache类

5a67e8ddcecc318d6fa5dd192be79159.png

可以看到Cache类调用函数的方法基本斗是这样, init()获取 到handler,然后操作handler对象,也就是我们真正的 操作对象,这里就是 phpredis类啦,

当然我们是没办法直接操作 phpredis类的, 只能使用Cache类 的寥寥几种方法,所以有些人不满意,因为队列,集和,哈希都认为没办法使用了,我也在网上看到有些同学 重写TP5的 redis类

其实大可不必, Cache类还是暴露了一个接口给我们的.

我们可以这样$res = Cache::init();

$redis = $res->handler();

$redis->lpush('test',111);

$redis->rpush('test',111);

$redis->lpop('test');

获得了 handler 也就是获得了 phpredis,这样就可以随便使用 phpredis原生的方法啦,而且还是单例模式哦, 没有新建对象额外的消耗

本文就到这里结束啦, 如果要知道更多Cache类的使用方法,可以按上文的方式直接看源代码,或者再去查阅官方文档.

虽然没有讲解如何使用,但是分析了 Cache的实现原理有助于提高我们的编程抽象水平, 上文分析源码的方式也同样可以用来分析其他的核心类库.

相关阅读:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/332732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA for Mac 项目窗口详解(Project Windows)

Show Members:显示成员,打勾,类的成员,例如:方法、字段等会显示出来,如下图所示: Show Excluded Files:显示排除的文件 Show Visibility Icons:显示可见性图标&#xff…

jdk9与jdk11哪个好_JDK 9、10和11中的安全性增强

jdk9与jdk11哪个好缩短JDK发布周期的原因之一是有可能推出更快的安全错误修复和增强功能。 在本文中,我们将简要回顾一下最新JDK版本中引入的主要安全增强功能。 由于这些增强功能中的大多数与TLS相关,因此必须了解TLS握手过程,如下图所示&am…

对一个java源文件进行正确编译,给定如下一个Java源文件Child.java,编译并运行Child.java,以下结果正确...

给定如下一个Java源文件Child.java,编译并运行Child.java,以下结果正确答:编译错误:没有找到构造器 Parent1()中国大学MOOC: 图像分割是基于灰度值的两个基本特性()答:相似性 连续性合同是平等主体之间的 法律行为。答…

多云平台_多云系统的授权

多云平台这是我目前正在使用的项目设计,用于消耗SPIFFE( 受所有人保护的安全生产身份框架( )的信任和身份识别,在WSO2的Prabath Siriwardena先生的启发下,在Moratuwa大学的Gihan Dias教授的指导下&#xf…

XML的学习

文章目录XML 简介XML 语法XML 组成部分XML 约束DTD名称解释引用内部的 DTD引用外部的 DTDSchema语法格式详解命名空间前缀的意义XML 解析解析 XML 的方式(思想)解析器JSOUP 的使用JSOUP 入门快捷查询Jsoup 选择器查询XPathXML 简介 XML,Exte…

php 日期多余小时,在PHP中添加小时到日期时间

DateTime是PHP中的一个很棒的功能$string 1/1/2016 11.00PM;$date new DateTime($string);$interval new DateInterval(PT6H);$date->add($interval);// Now add another 6 hours while we are between 12:00 AM and 6:00 AMwhile($date->format(G) > 0 &&…

streaming api_通过Spring Integration消费Twitter Streaming API

streaming api1.概述 众所周知, Spring Integration具有用于与外部系统交互的大量连接器。 Twitter也不例外,而且很长一段时间以来,因为Spring Social一直是一个开箱即用的解决方案,Spring Integration利用该解决方案来连接到社交…

Linux命令之 mount -- 文件系统挂载

文章目录简介参考实例加载指定的分区参考简介 mount 命令用于加载文件系统到指定的加载点。此命令的最常用于挂载 cdrom,使我们可以访问 cdrom 中的数据,因为你将光盘插入 cdrom 中,Linux 并不会自动挂载,必须使用 Linux mount 命…

java 舍,java 4舍六入五成双

java 四舍六入五成双1. 小于5舍去,即舍去部分的数值小于保留部分的末位的半个单位,则末位不变;2. 大于5进1,即舍去部分的数值大于保留部分的末位的半个单位,则末位加1;3. 等于5时取偶数,即舍去部…

Linux命令之 umount -- 卸载文件系统

文章目录介绍参考介绍 umount 是“unmount”的缩写,译为“不挂载。所以它的”的作用是卸载已挂载的文件系统、目录或文件。 利用设备名或挂载点都能umount文件系统,不过最好还是通过挂载点卸载,以免使用绑定挂载(一个设备&#x…

aws技术峰会2018_AWS re:Invent 2018的5大公告

aws技术峰会2018AWS re:Invent刚刚完成。 这是一个巨大的活动,在拉斯维加斯7家最大的酒店中,有50,000多名与会者,并发布了许多新的服务公告。 无服务器端通过新的lambda增强功能和更好的容器支持继续受到很多关注。 AWS通过新的“…

php 降低图像大小,PHP图像重新调整大小

我有一个PHP脚本,可以重新调整JPEG图像的大小.但是,由于某种原因,图像被扭曲,即使我将其编程为按比例计算x或y(取决于照片方向).质量是100,所以我不明白为什么它会使它们扭曲.我究竟做错了什么?编辑原始图像为3264px x 2448px谢谢代码:$im ImageCreateF…

Linux命令之 mke2fs -- 格式化分区(为分区写入文件系统)

文章目录简介命令选项参考实例格式化指定的分区简介 在磁盘分区上创建 ext2、ext3、ext4 等文件系统,默认情况下会创建 ext2。 虽然 mkfs 命令非常简单易用,但其不能调整分区的默认参数(比如块大小是 4096 Bytes),这…

Linux格式化分区的命令

查看系统所有的文件系统: [~]$ df -ah Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1s5s1 234Gi 15Gi 65Gi 19% 575614 682553320 0% / devfs 190Ki 190Ki 0Bi 100% 658 …

java jwt 验证_教程:用Java创建和验证JWT

java jwt 验证“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。 Java对JWT(JSON Web令牌)的支持过去需要进行大量…

php网页布局边框,用CSS来设置网页当中的边框

摘要:跟着老师视频做的一个css边框小练习 css——边框 &nbs跟着老师视频做的一个css边框小练习 css——边框 .box{width:100px;height:100px;border:1px solid #ccc;border-radius:20px;}.main{width:100px;height:100px;border-top: 1px s…

Linux命令之 mkfs -- 在特定的分区创建 Linux 文件系统

文章目录简介选项参考示例在 /dev/hda5 上建一个 msdos 的文件系统,同时检查是否有坏轨存在,并且将过程详细列出来:将指定分区格式化成各种类型的文件系统简介 该命令用来在特定的分区创建 Linux 文件系统,常见的文件系统有 ext2…

本地线程分配缓冲_线程本地分配缓冲区

本地线程分配缓冲最近,我一直在研究遭受严重性能问题的Java应用程序。 在许多问题中,真正引起我注意的一个问题是新对象的分配速度相对较慢(应用程序分配了大量的相当大的对象)。 后来发现,原因是大量的分配发生在TLAB…

mysql 触发器计算总价,mysql’插入’触发器根据其他字段计算字段

我正在尝试创建一个触发器,它将根据用户输入的lat / lng列更新GEOMETRY列.我的触发器看起来像这样 –CREATE TRIGGER tbl.fooAFTER INSERT ON tbl FOR EACH ROWBEGINUPDATE tblSET coord Point(lng, lat)WHERE id NEW.id;END但是,当我插入一个带有lng,lat值的新行时,我收到以…