静态化API能够理解成把一些接口的数据存储在服务器本地。经常使用的是存成json文件,也能够是放在swoole的table中,总之是用户不从数据库直接读取数据,而是从本地加载的方式来大幅提升性能,由于不少系统的性能瓶颈是在数据库的位置。php
这里作的分页的场景,不包含分页的源码,只从拿到了分页的数据看看定时生成json和获取json的部分前端
这是原始的方法,每一个用户访问都会去数据库里面读取一次,每一次分页也会访问数据库,会形成大量的资源开销。mysql
public function lists0(){ $condition = []; if(!empty($this->params['cat_id'])){ $condition['cat_id'] = intval($this->params['cat_id']); } try { $videoModel = new VideoModel(); $data = $videoModel->getVideoData($condition, $this->params['page'], $this->params['size']); } catch (\Exception $e) { //$e->getMessage(); return $this->writeJson(Status::CODE_BAD_REQUEST,"服务异常"); } if(!empty($data['lists'])){ foreach ($data['lists'] as &$list){ $list['create_time'] = date("Ymd H:i:s",$list['create_time']); $list['video_duration'] = gmstrftime("%H:%M:%S",$list["video_duration"]); } } return $this->writeJson(Status::CODE_OK,'OK',$data); }
知识点:laravel
1 获取以秒飞单位的时间长度使用gmstrftime(“%H:%M:%S”,$list[“video_duration”])web
2 在作模型的时候写一个基类,把链接数据库的工做放在这个基类的构造方法当中,这样每次实例化的时候就自动链接了,提升代码的复用性redis
<?php /** * Created by bingxiong. * Date: 12/23/19 * Time: 1:20 AM * Description: */ namespace App\Model; use EasySwoole\Core\Component\Di; class Base { public $db = ""; public function __construct() { if(empty($this->tableName)){ throw new \Exception("table error"); } $db = Di::getInstance()->get("MYSQL"); if($db instanceof \MysqliDb){ $this->db = $db; }else{ throw new \Exception("db error"); } } public function add($data){ if(empty($data) || !is_array($data)){ return false; } return $this->db->insert($this->tableName,$data); } }
这是直接读取静态化API的方法,其实就是读文件而后返回给前端sql
/** * 第二套方法 - 直接读取静态化json数据 * @return bool */ public function lists(){ $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0; $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json"; $videoData = is_file($videoFile) ? file_get_contents($videoFile) : []; var_dump($videoData); $videoData = !empty($videoData) ? json_decode($videoData,true) : []; $count = count($videoData); // var_dump($videoData); return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData)); }
这里的getPagingData使用了array_slice来切割数组来作分页。shell
/** * 获取分页信息 * @param $count * @param $data * @return array */ public function getPagingData($count, $data){ $totalPage = ceil($count / $this->params['size']); $data = $data ?? []; $data = array_slice($data, $this->params['from'], $this->params['size']); return [ 'total_page' => $totalPage, 'page_size' => $this->params['page'], 'count' => intval($count), 'list' => $data ]; }
定时生成Json文件代码 – 核心部分 全局事件easySwooleEvent->mainServiceCreate中执行,这样easySwoole启动了以后就会执行,这里使用的原生的crontab,只可以精确到分数据库
$cacheVideoObj = new VideoCache(); // 使用cronTab处理定时任务 // 这里是闭包 要use $cacheVideoObj以后才能获取,实例化不放在function中是为了防止每次都实例化浪费资源 CronTab::getInstance() ->addRule("test_bing_crontab", '*/1 * * * *', function () use($cacheVideoObj) { $cacheVideoObj->setIndexVideo(); });
也能够使用easySwoole的定时器来实现也是放在mainServiceCreate中执行,这里的代码必定要注意要注册一个onWorkerStart。json
而后指定一个进程去执行这个定时任务注意一下这里闭包里面又有一个闭包,外面的变量要use两次。
必定要注意easwoole定时器的使用,这里的坑比较多必定要注意,优点是swoole的定时器能够到毫秒级而contab只能到分级
// easySwoole自带定时器 $register->add(EventRegister::onWorkerStart,function (\swoole_server $server,$workerId)use($cacheVideoObj){ //让第一个进程去执行,不然每一个进程都会执行 if($workerId == 0){ Timer::loop(1000*2,function ()use ($cacheVideoObj){ $cacheVideoObj->setIndexVideo(); }); } });
swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。性能十分强悍。使用起来有一点像是缓存。
这里再也不是生成一个json文件去读取了,
读取table中数据给前端的方法,注意要use cache这个类,这里直接使用get就能够根据key获取到table中的数据,注意这个是一个单例模式,所以须要getInstance
public function lists(){ $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0; $videoFile = EASYSWOOLE_ROOT."/webroot/video/json/".$catId.".json"; // 第三套方案 table $videoData = Cache::getInstance()->get("index_video_data_cat_id".$catId); $videoData = !empty($videoData) ? $videoData : []; $count = count($videoData);; return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData)); }
设置直接set(key,data)就能够了,注意这里的代码不是很严谨,好比要判断一下是否是设置成功了,没有设置成功发短信之类的,也要处理一下空值的场景,这里只是作一个演示。
<?php /** * Created by bingxiong. * Date: 12/23/19 * Time: 10:13 PM * Description: */ namespace App\Lib\Cache; use App\Model\Video as VideoModel; use EasySwoole\Config; use EasySwoole\Core\Component\Cache\Cache; class Video { public function setIndexVideo() { $catIds = array_keys(Config::getInstance()->getConf("category")); array_unshift($catIds, 0); $modelObj = new VideoModel(); foreach ($catIds as $catId) { $condition = []; if (!empty($catId)) { $condition['cat_id'] = $catId; } try { $data = $modelObj->getVideoCacheData($condition); } catch (\Exception $e) { // 短信报警 $data = []; } if (empty($data)) { } foreach ($data as &$list) { $list['create_time'] = date("Ymd H:i:s", $list["create_time"]); $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]); } // 因为table存在内存因此重启服务器时数据会丢失,要将配置中的PERSISTENT_TIME设置为1进行落盘操做 Cache::getInstance()->set("index_video_data_cat_id".$catId, $data); } } }
注意,必定要去config里面开启
'PERSISTENT_TIME'=>1//若是须要定时数据落地,请设置对应的时间周期,单位为秒
不然会在重启服务的时候没有数据,由于每次启动的时候swoole table会清空,要等到定时去set table的时候才会有数据,所以要开启数据落盘,这样会生成两个文件:
每次启动的时候因为尚未执行定时任务,就会先读取这两个落盘的文件中的数据,防止服务启动时等待table生成形成业务中断。
用redis来作数据缓存,每次从缓存里面度读
先重写一下set方法,更加严谨一点
/** * 重写set方法 处理一下失效时间以及数组转json * @param $key * @param $value * @param $time * @return bool|string */ public function set($key, $value, $time){ if(empty($key)){ return ''; } if(is_array($value)){ $value = json_encode($value); } if(!$time){ return $this->redis->set($key,$value); } return $this->redis->setex($key, $time, $value); }
使用起来很简单啦,在以前的代码中
Di::getInstance()->get("REDIS")->set("index_video_data_cat_id".$catId, $data);
而后取出的数据的部分
public function lists(){ $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0; //redis $videoData = Di::getInstance()->get("REDIS")->get("index_video_data_cat_id".$catId); $videoData = !empty($videoData) ? json_decode($videoData,true) : []; $count = count($videoData); return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData)); }
高度封装
只须要在配置文件中进行配置便可选择相应方法
设置静态化API和获取静态化API的方法
<?php /** * Created by bingxiong. * Date: 12/23/19 * Time: 10:13 PM * Description: */ namespace App\Lib\Cache; use App\Model\Video as VideoModel; use EasySwoole\Config; use EasySwoole\Core\Component\Cache\Cache; use EasySwoole\Core\Component\Di; use EasySwoole\Core\Http\Message\Status; class Video { /** * 设置静态API的方法 * @throws \Exception */ public function setIndexVideo() { $catIds = array_keys(Config::getInstance()->getConf("category")); array_unshift($catIds, 0); // 获取配置 try { $cacheType = Config::getInstance()->getConf("base.indexCacheType"); } catch (\Exception $e) { return $this->writeJson(Status::CODE_BAD_REQUEST,"请求失败"); } $modelObj = new VideoModel(); foreach ($catIds as $catId) { $condition = []; if (!empty($catId)) { $condition['cat_id'] = $catId; } try { $data = $modelObj->getVideoCacheData($condition); } catch (\Exception $e) { // 短信报警 $data = []; } if (empty($data)) { } foreach ($data as &$list) { $list['create_time'] = date("Ymd H:i:s", $list["create_time"]); $list["video_duration"] = gmstrftime("%H:%M:%s", $list["video_duration"]); } switch ($cacheType) { case 'file': $res = file_put_contents($this->getVideoCatIdFile($catId), json_encode($data)); break; case 'table': $res = Cache::getInstance()->set($this->getCatKey($catId), $data); break; case 'redis': $res = Di::getInstance()->get("REDIS")->set($this->getCatKey($catId), $data); break; default: throw new \Exception("请求不合法"); break; } if(empty($res)){ // 记录日志 // 报警 } } } /** * 获取数据的方法 * @param $catId * @return array|bool|mixed|null|string * @throws \Exception */ public function getCache($catId){ $cacheType = Config::getInstance()->getConf("base.indexCacheType"); switch ($cacheType){ case 'file': $videoFile = $this->getVideoCatIdFile($catId); $videoData = is_file($videoFile) ? file_get_contents($videoFile) : []; $videoData = !empty($videoData) ? json_decode($videoData,true) : []; break; case 'table': $videoData = Cache::getInstance()->get($this->getCatKey($catId)); $videoData = !empty($videoData) ? $videoData : []; break; case 'redis': $videoData = Di::getInstance()->get("REDIS")->get($this->getCatKey($catId)); $videoData = !empty($videoData) ? json_decode($videoData,true) : []; break; default: throw new \Exception("请求不合法"); break; } return $videoData; } public function getVideoCatIdFile($catId = 0){ return EASYSWOOLE_ROOT . "/webroot/video/json/" . $catId . ".json"; } public function getCatKey($catId = 0){ return "index_video_data_cat_id".$catId; } }
只需修改配置文件
<?php /** * Created by bingxiong. * Date: 12/24/19 * Time: 5:12 PM * Description: */ return [ "indexCacheType" => "redis" // redis file table ];
控制器获取数据给前端
public function lists(){ $catId = !empty($this->params['cat_id']) ? intval($this->params['cat_id']) : 0; $videoData = (new VideoCache())->getCache($catId); $count = count($videoData); return $this->writeJson(Status::CODE_OK,'OK',$this->getPagingData($count,$videoData)); }
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是人才。以前说过,PHP方面的技术点不少,也是由于太多了,实在是写不过来,写过来了你们也不会看的太多,因此我这里把它整理成了PDF和文档,若是有须要的能够
更多学习内容能够访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)
以上内容但愿帮助到你们,不少PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提高,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货须要的能够免费分享给你们,须要的能够加入个人 PHP技术交流群