Redis鍵值過期自動執行回調函數


用到了 redis 的鍵空間通知(keyspace notifications)

  • 今天幫忙解決問題時遇到的redis一個功能點

  • 具體行為就是:某個鍵值到了過期時間自動觸發回調函數,然后執行一些操作,比如訂單15分鍾未支付就自動取消。

系統環境Win10, PHP7.1

  • 下面記錄下剛才爬的坑:

    • 1、redis 2.8版本的升級到 3.0 以上再說,比如 3.2
    • 2、如果將redis加入到windows的系統服務了,建議在初始階段停止服務,使用redis-server "配置文件路徑" 來啟動redis,因為系統服務啟動的redis使用的配置文件你可能搞不清楚,導致修改配置文件不生效,在最后我會講。
    • 3、各種路徑,參數問題,加不加引號等問題。
  • 本文僅就此功能進行講解,業務邏輯不過多累述。

首先找一個空目錄,新建四個文件,文件名以及內容如下:

  • index.php 經過一些業務邏輯后,設置一個 10s 后過期的鍵值
<?php
require_once 'Redis2.class.php';
// require_once 'db.class.php';
 
$redis2 = new \Redis2();
// $mysql      = new \mysql();   // 注釋是你的業務邏輯
// $mysql->connect();
 
// $order_sn='SN'.time().'T'.rand(10000000,99999999);
// $data = ['ordersn'=>$order_sn,'status'=>0,'createtime'=>date('Y-m-d H:i:s',time())];
// $mysql->insert('order',$data);
 
$res = $redis2->setex('kkk',10, "It is no pay");
var_dump($res);exit;
  • psubscribe.php 監聽,超時回調函數
<?php
require_once 'Redis2.class.php';
 
ini_set('default_socket_timeout', -1);  //不超時
$redis = new Redis2();
// 解決Redis客戶端訂閱時候超時情況
$redis->setOption();
$redis->psubscribe(array('__keyevent@0__:expired'), 'keyCallback');
// 回調函數,這里寫處理邏輯
function keyCallback($redis, $pattern, $chan, $msg)
{
	echo date('Y-M-D H:i:s');   // 這兩行留作查看是否執行回調函數
	// file_put_contents("d:/lizheng.log", "\n\n".print_r(123, true),8);

    // 下面寫你的業務邏輯
    //修改訂單狀態
    // $order=model('order')->where(['order_no'=>$msg])->find();
    // $order->status=-1;
    // $order->save();
    // //庫存還原
    // $products=json_decode($order->snap_items,true);
    // foreach($products as $v){
    //     model('product')->where(['id'=>$v['id']])->setInc('stock',$v['count']);
    // }
}
  • Redis2.class.php 封裝 redis 類
<?php
class Redis2
{
    private $redis;
 
    public function __construct($host = '127.0.0.1', $port = 6379)
    {
        $this->redis = new Redis();
        $this->redis->connect($host, $port);
        // $this->redis->auth('123456');
    }
 
    public function setex($key, $time, $val)
    {
        return $this->redis->setex($key, $time, $val);
    }
 
    public function set($key, $val)
    {
        return $this->redis->set($key, $val);
    }
 
    public function get($key)
    {
        return $this->redis->get($key);
    }
 
    public function expire($key = null, $time = 0)
    {
        return $this->redis->expire($key, $time);
    }
 
    public function psubscribe($patterns = array(), $callback)
    {
        $this->redis->psubscribe($patterns, $callback);
    }
 
    public function setOption()
    {
        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
    }
 
    public function lRange($key,$start,$end)
    {
        return $this->redis->lRange($key,$start,$end);
    }
 
    public function lPush($key, $value1, $value2 = null, $valueN = null ){
        return $this->redis->lPush($key, $value1, $value2 = null, $valueN = null );
    }
 
    public function delete($key1, $key2 = null, $key3 = null)
    {
        return $this->redis->delete($key1, $key2 = null, $key3 = null);
    }
 
}
  • db.class.php 封裝mysql, 這里我們不涉及業務邏輯所以未使用
<?php
class mysql
{
    private $mysqli;
    private $result;
    /**
     * 數據庫連接
     * @param $config 配置數組
     */
 
    public function connect()
    {
        $config=array(
            'host'=>'127.0.0.1',
            'username'=>'root',
            'password'=>'123456qwerty',
            'database'=>'marhal',
            'port'=>3306,
            );
 
        $host = $config['host'];    //主機地址
        $username = $config['username'];//用戶名
        $password = $config['password'];//密碼
        $database = $config['database'];//數據庫
        $port = $config['port'];    //端口號
        $this->mysqli = new mysqli($host, $username, $password, $database, $port);
 
    }
    /**
     * 數據查詢
     * @param $table 數據表
     * @param null $field 字段
     * @param null $where 條件
     * @return mixed 查詢結果數目
     */
    public function select($table, $field = null, $where = null)
    {
        $sql = "SELECT * FROM `{$table}`";
        //echo $sql;exit;
        if (!empty($field)) {
            $field = '`' . implode('`,`', $field) . '`';
            $sql = str_replace('*', $field, $sql);
        }
        if (!empty($where)) {
            $sql = $sql . ' WHERE ' . $where;
        }
 
 
        $this->result = $this->mysqli->query($sql);
 
        return $this->result;
    }
    /**
     * @return mixed 獲取全部結果
     */
    public function fetchAll()
    {
        return $this->result->fetch_all(MYSQLI_ASSOC);
    }
    /**
     * 插入數據
     * @param $table 數據表
     * @param $data 數據數組
     * @return mixed 插入ID
     */
    public function insert($table, $data)
    {
        foreach ($data as $key => $value) {
            $data[$key] = $this->mysqli->real_escape_string($value);
        }
        $keys = '`' . implode('`,`', array_keys($data)) . '`';
        $values = '\'' . implode("','", array_values($data)) . '\'';
        $sql = "INSERT INTO `{$table}`( {$keys} )VALUES( {$values} )";
        $this->mysqli->query($sql);
        return $this->mysqli->insert_id;
    }
    /**
     * 更新數據
     * @param $table 數據表
     * @param $data 數據數組
     * @param $where 過濾條件
     * @return mixed 受影響記錄
     */
    public function update($table, $data, $where)
    {
        foreach ($data as $key => $value) {
            $data[$key] = $this->mysqli->real_escape_string($value);
        }
        $sets = array();
        foreach ($data as $key => $value) {
            $kstr = '`' . $key . '`';
            $vstr = '\'' . $value . '\'';
            array_push($sets, $kstr . '=' . $vstr);
        }
        $kav = implode(',', $sets);
        $sql = "UPDATE `{$table}` SET {$kav} WHERE {$where}";
 
        $this->mysqli->query($sql);
        return $this->mysqli->affected_rows;
    }
    /**
     * 刪除數據
     * @param $table 數據表
     * @param $where 過濾條件
     * @return mixed 受影響記錄
     */
    public function delete($table, $where)
    {
        $sql = "DELETE FROM `{$table}` WHERE {$where}";
        $this->mysqli->query($sql);
        return $this->mysqli->affected_rows;
    }
}

配置文件修改

    配置文件中找到 notify-keyspace-events ""  將其改成   notify-keyspace-events "Ex" // 注意引號, 之所以要求3.0以上版本,是因為2.8版本設置了此項后會於java沖突,無法啟動。
    Ex代表過期,另外注意修改的配置文件,是否為正使用的配置文件
  • 以上操作完成后,重啟redis,以指定配置文件的方式重啟,以免使用其它的配置文件或redis默認配置。
    redis-server "D:/redis/redis.windows.conf";  // 3.2版本為例,其它版本中,文件名可能為其它。
    // 此處之外還有一個 redis.windows.service.conf 文件,作為系統服務啟動時的配置文件。
  • 服務截圖如下

    • "D:\Program Files\redis\redis-server.exe" --service-run redis.windows-service.conf --loglevel verbose
  • 如果你不想將redis加入到系統服務,那么可以不用安裝服務,只用命令行啟動redis即可。

    redis-server.exe --service-uninstall    // redis系統服務的卸載, 也可以通過管理員命令行 sc delete 服務名 來卸載
    redis-server --service-install redis.windows.conf --loglevel verbose   // 安裝服務

運行測試

  • 切換到你的redis目錄,或者將redis加入系統變量Path中
  • php -v // 可以直接調用PHP命令,我的版本為7.1,此功能與php版本關系不大
  • php psubscribe.php // 啟動監聽程序
  • php index.php // 通過index.php 模擬訂單超時,即建立一個生命周期為10秒的鍵值。
  • 10秒后,psubscribe.php 會調用回調函數執行一些操作。
  • psubscribe.php 窗口有相應的輸出即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM