01.03 深入淺出:Swoole單例模式及依賴注入進行Redis底層類庫封裝


redis安裝及php-redis擴展安裝初步使用封裝基類 – 單例模式優化封裝 – 依賴注入從配置加載從自定義配置加載
本文將一步一步的從redis的安裝一直到easySwoole使用高度封裝的底層類庫,並進行優化提高代碼的維護性,可以直接看最後的優化結果

第一部分 redis安裝及php-redis擴展安裝

redis的安裝很簡單,直接到redis官網下載,這裡使用的是redis4.0.12,下載後直接make && make install即可,
進入到redis的文件夾的src目錄,運行一個redis服務,其默認端口是6379:

<code>./redis-server/<code>

運行一個redis客戶端

<code>./redis-cli/<code>

安裝php-redis也很簡單,從github把擴展下載下來解壓,然後

<code>phpize
./configure
make && make install/<code>

即可,如果想要configure到指定的php版本就指定phpize的目錄並在config的時候prefix到指定的php配置文件,安好之後再php.ini加上redis.so即可。

第二部分 初步使用

直接在源碼中進行redis的連接:

<code>/**
* Created by bingxiong.
* Date: 12/19/18


* Time: 6:38 PM
* Description:
*/

namespace App\\HttpController\\Api;

use \\EasySwoole\\Core\\Component\\Di;
// 使用封裝的redis基類
use App\\Lib\\Redis\\Redis;

class Index extends Base
{
public function getRedis(){
$redis = new \\Redis();
$redis->connect("127.0.0.1",6379, 5);
$redis->set("bing",19249);
return $this->writeJson(200,'OK',$redis->get("bing"));
}

}/<code>

顯然我們不可能這樣使用,需要對其進行二次封裝

第三部分 創建Redis的基類

基類創建在App->Lib->Redis->redis.php

<code>/**
* Created by bingxiong.
* Date: 12/21/18
* Time: 12:44 AM
* Description:
*/

namespace App\\Lib\\Redis;

// 使用單例模式
use EasySwoole\\Config;
use EasySwoole\\Core\\AbstractInterface\\Singleton;

class Redis
{
use Singleton;

public $redis = "";

private function __construct()


{
//判斷擴展有沒有安裝
if(!extension_loaded('redis')){
throw new \\Exception("redis.so文件不存在");
}
try{
$this->redis = new \\Redis();
$result = $this->redis->connect("127.0.0.1",6379,3);
} catch (\\Exception $e){
throw new \\Exception("redis服務異常");
}

if($result === false){
throw new \\Exception("redis連接失敗");
}
}

/**
* 重寫get友好的返回key不存在的情況
* @param $key
* @return bool|string
*/
public function get($key){
if(empty($key)){
return '';
}
return $this->redis->get($key);
}

}/<code>

說明:
使用了單例模式:用於為一個類生成一個唯一的對象。最常用的地方是數據庫連接。 使用單例模式生成一個對象後,該對象可以被其它眾多對象所使用。作為對象的創建模式,單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱為單例類。1、一個類只能有一個實例 2、它必須自行創建這個實例 3、它必須自行向整個系統提供這個實例


有了基類之後,配置和連接的任務就從基類的構造函數加載了,之前的代碼就變成了

<code>/**
* Created by bingxiong.
* Date: 12/19/18
* Time: 6:38 PM
* Description:
*/

namespace App\\HttpController\\Api;


use \\EasySwoole\\Core\\Component\\Di;
// 使用封裝的redis基類
use App\\Lib\\Redis\\Redis;

class Index extends Base
{
public function getRedis(){
$result = Redis::getInstance()->get('bing');
return $this->writeJson(200,'OK',$result);
}

}/<code>

第四部分 依賴注入

依賴注入:一個對象提供另一個對象的依賴關係,是一種設計模式,好處就是有效的分離了對象和它所需要的外部資源,使得它們鬆散耦合,有利於功能複用,更重要的是使得程序的整個體系結構變得非常靈活。
easyswool使用依賴注入非常的簡單,在easyswoolevent的文件中

<code>public static function mainServerCreate(ServerManager $server,EventRegister $register): void
{
Di::getInstance()->set('MYSQL',\\MysqliDb::class,Array (
'host' => '127.0.0.1',
'username' => 'root',


'password' => 'root',
'db'=> 'imooc_video',
'port' => 8889,
'charset' => 'utf8')
);
Di::getInstance()->set('REDIS',Redis::getInstance());

}/<code>

上面的是我數據庫的配置,下面getInstance中實例化了剛才封裝的基類

實例化di再getInstance就可以了

<code>/**
* Created by bingxiong.
* Date: 12/19/18
* Time: 6:38 PM
* Description:
*/

namespace App\\HttpController\\Api;


use \\EasySwoole\\Core\\Component\\Di;
// 使用封裝的redis基類
use App\\Lib\\Redis\\Redis;

class Index extends Base
{

public function getRedis(){
// 3使用依賴注入
$result = Di::getInstance()->get('REDIS')->get('bing');
return $this->writeJson(200,'OK',$result);
}

}/<code>

第5部分 從配置加載

在配置文件中添加,或者用你喜歡的方式配置

<code>'REDIS' => [
'host' => '127.0.0.1',
'port' => 6379,


'time_out' => 3
]/<code>

然後基類改寫成

<code>/**
* Created by bingxiong.
* Date: 12/21/18
* Time: 12:44 AM
* Description:
*/

namespace App\\Lib\\Redis;

// 使用單例模式
use EasySwoole\\Config;
use EasySwoole\\Core\\AbstractInterface\\Singleton;

class Redis
{
use Singleton;

public $redis = "";

private function __construct()
{
//判斷擴展有沒有安裝
if(!extension_loaded('redis')){
throw new \\Exception("redis.so文件不存在");
}
try{
//從配置加載REDIS - 提高維護性
$redisConfig = Config::getInstance()->getConf("REDIS");
$this->redis = new \\Redis();
$result = $this->redis->connect($redisConfig['host'],$redisConfig['port'],$redisConfig['time_out']);
} catch (\\Exception $e){
throw new \\Exception("redis服務異常");
}

if($result === false){
throw new \\Exception("redis連接失敗");
}
}

/**
* 重寫get友好的返回key不存在的情況


* @param $key
* @return bool|string
*/
public function get($key){
if(empty($key)){
return '';
}
return $this->redis->get($key);
}

}/<code>

第6部分 從自定義配置加載

由於可能有很多配置,把配置都放在這個文件的話會顯得很臃腫,因此我們將每個配置文件比如redis mysql elasticsearch的配置單獨抽離出來單獨管理,這樣有更好的維護性

所以在根目錄創建config->redis.php,在這裡直接返回redis的配置

<code>/**
* Created by bingxiong.
* Date: 12/21/18
* Time: 2:10 AM
* Description: redis相關配置
*/

return ['host' => '127.0.0.1', 'port' => 6379, 'time_out' => 3];/<code>

然後自定義載入配置文件,創建了一個loadConf方法,這個方法在easyswoole的文檔中有,所以最終的

EasySwooleEvent.php

<code>/**
* Created by PhpStorm.
* User: yf
* Date: 2018/1/9
* Time: 下午1:04
*/

namespace EasySwoole;



use App\\Lib\\Redis\\Redis;
use \\EasySwoole\\Core\\AbstractInterface\\EventInterface;
use EasySwoole\\Core\\Component\\Di;
use \\EasySwoole\\Core\\Swoole\\ServerManager;
use \\EasySwoole\\Core\\Swoole\\EventRegister;
use \\EasySwoole\\Core\\Http\\Request;
use \\EasySwoole\\Core\\Http\\Response;
use \\EasySwoole\\Core\\Utility\\File;

Class EasySwooleEvent implements EventInterface {

public static function frameInitialize(): void
{
// TODO: Implement frameInitialize() method.
date_default_timezone_set('Asia/Shanghai');
self::loadConf(EASYSWOOLE_ROOT.'/Config');
}

public static function loadConf($ConfPath)
{
$Conf = Config::getInstance();
$files = File::scanDir($ConfPath);
foreach ($files as $file) {
$data = require_once $file;
$Conf->setConf(strtolower(basename($file, '.php')), (array)$data);
}
}

/**
* 使用依賴注入加載
* @param ServerManager $server
* @param EventRegister $register
*/
public static function mainServerCreate(ServerManager $server,EventRegister $register): void
{
Di::getInstance()->set('MYSQL',\\MysqliDb::class,Array (
'host' => '127.0.0.1',
'username' => 'root',
'password' => 'root',
'db'=> 'imooc_video',
'port' => 8889,
'charset' => 'utf8')
);
Di::getInstance()->set('REDIS',Redis::getInstance());

}

public static function onRequest(Request $request,Response $response): void
{

// TODO: Implement onRequest() method.
}

public static function afterAction(Request $request,Response $response): void
{
// TODO: Implement afterAction() method.
}
}/<code>

redis.php 這是redis的基類
/**

Created by bingxiong.Date: 12/21/18Time: 12:44 AMDescription:
*/

namespace App\\Lib\\Redis;

// 使用單例模式
use EasySwoole\\Config;
use EasySwoole\\Core\\AbstractInterface\\Singleton;

class Redis
{
use Singleton;

<code>public $redis = "";

private function __construct()
{
//判斷擴展有沒有安裝
if(!extension_loaded('redis')){
throw new \\Exception("redis.so文件不存在");
}
try{
// 從自己的配置加載
$redisConfig = Config::getInstance()->getConf("redis");
$this->redis = new \\Redis();
$result = $this->redis->connect($redisConfig['host'],$redisConfig['port'],$redisConfig['time_out']);
} catch (\\Exception $e){
throw new \\Exception("redis服務異常");
}

if($result === false){
throw new \\Exception("redis連接失敗");
}
}

/**
* 重寫get友好的返回key不存在的情況


* @param $key
* @return bool|string
*/
public function get($key){
if(empty($key)){
return '';
}
return $this->redis->get($key);
}/<code>

}
最終實現

<code>/**
* Created by bingxiong.
* Date: 12/19/18
* Time: 6:38 PM
* Description:
*/

namespace App\\HttpController\\Api;

use \\EasySwoole\\Core\\Component\\Di;

class Index extends Base
{
public function getRedis(){
// 使用依賴注入
$result = Di::getInstance()->get('REDIS')->get('bing');
return $this->writeJson(200,'OK',$result);
}

}/<code>

本文作者熊冰,個人網站Bing的天涯路),轉載請註明出處。