What is a static API?
A static API can be understood as storing some interface data locally on the server. It can be stored in a JSON file, or it can be stored in a Swoole table, but the user doesn’t read the data directly from the database, but loads it locally to dramatically improve performance, because the performance bottleneck of many systems is in the database location.
The solution
Solution 1 easySwoole + Crontab 2 easySwoole timer 3 Swoole table 4 Redis
implementation
Here to do the paging scenario, does not contain the source of paging, only from the data to take a look at the timing of the generation of JSON and get JSON part
The original method – read mysql
This is the original method, each user access to the database once, each page also access the database, resulting in a large amount of resource overhead.
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," service exception "); } 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); }Copy the code
Knowledge:
Gmstrftime (” %H:%M:%S “,$list[” video_duration “])
2. Write a base class when making the model, and put the work of connecting to the database in the constructor of the base class, so that each instantiation will automatically connect, improve the reuse of the code
<? 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); }}Copy the code
Scheme 1 and 2 use easySwoole timer and CONtab generated static API
This is the way to read the static API directly, essentially reading the file and returning it to the front end
* @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)); }Copy the code
GetPagingData here uses array_slice to slice the array for paging.
@param $data * @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 ]; }Copy the code
The core global event easySwooleEvent->mainServiceCreate will be executed when easySwoole is started. The native crontab used here is only accurate to the point
$cacheVideoObj = new VideoCache(); CronTab::getInstance(); // Use $cacheVideoObj to execute a scheduled task ->addRule("test_bing_crontab", '*/1 * * * *', function () use($cacheVideoObj) { $cacheVideoObj->setIndexVideo(); });Copy the code
You can also use easySwoole’s timer and run it in mainServiceCreate, so it’s important to register an onWorkerStart.
And then you specify a process to perform the scheduled task and notice that there’s another closure inside the closure, and the variables outside are used twice.
It’s important to be aware of the easwoole timer, because there are a lot of pits, but the advantage is that the Swoole timer can go to the millisecond level whereas the Contab timer can only go to the level
/ / easySwoole built-in timer $register - > add (EventRegister: : onWorkerStart, function (\ swoole_server $server,$workerId)use($cacheVideoObj){if($workerId == 0){Timer::loop(1000*2,function ()use ($cacheVideoObj){ $cacheVideoObj->setIndexVideo(); }); }});Copy the code
Scheme 3 Swoole table solution
Swoole_table an ultra-high performance, concurrent data structure based on shared memory and locking implementation. Used to solve multi-process/multi-thread data sharing and synchronization locking problems. Very powerful performance. It works a little bit like a cache.
Instead of generating a JSON file to read,
Use cache to fetch data from a table by key. This is a singleton, so getInstance is required
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)); }Copy the code
Set (key,data) directly, note that the code here is not very rigorous, such as to determine whether the setting is successful, not successful SMS, etc., also need to deal with the null scenario, here is just a demonstration.
<? 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"]); } // The table has memory, so the data will be lost when the server restarts. PERSISTENT_TIME = 1 Cache::getInstance()->set("index_video_data_cat_id".$catId, $data); }}}Copy the code
Be sure to open it in config
'PERSISTENT_TIME'=>1// If periodic data is required to be landed, set the corresponding time period, in secondsCopy the code
Otherwise, there will be no data when the service is restarted, because the Swoole table will be cleared every time it is started, and there will be data only when the set table is scheduled. Therefore, the data drop disk should be enabled, which will generate two files:
When the scheduled task is not executed, the system reads the data in the two disks to prevent service interruption caused by table generation during service startup.
Plan 4 Redis
Redis to do the data cache, each time from the cache inside the read
So let’s rewrite the set method, just to be a little bit more rigorous
/ * * * to rewrite the set method to deal with the failure time and turn the json array * @ $key param * @ $value * @ param param * @ $time return a 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); }Copy the code
It’s pretty easy to use, in the previous code
Di::getInstance()->get("REDIS")->set("index_video_data_cat_id".$catId, $data);
Copy the code
Then pull out the part of the 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)); }Copy the code
High encapsulation
You only need to configure in the configuration file to select the corresponding method
Set the static API and the method to get it
<? 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; Public function setIndexVideo() {$catIds = $catIds = $catIds = $catIds = array_keys(Config::getInstance()->getConf("category")); array_unshift($catIds, 0); $cacheType = Config::getInstance()->getConf("base.indexCacheType"); } catch (\Exception $e) {return $this->writeJson(Status::CODE_BAD_REQUEST," request failed "); } $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(" request invalid "); break; } the if (empty ($res)) {/ / log / / alarm}}} / * * * * to get the data of method @ $catId param * @ the 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(" request invalid "); 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; }}Copy the code
Just modify the configuration file
<? php /** * Created by bingxiong. * Date: 12/24/19 * Time: 5:12 PM * Description: */ return [ "indexCacheType" => "redis" // redis file table ];Copy the code
The controller gets the data to the front end
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)); }Copy the code