PHP - Redis實現入隊出隊

一、需知

1、入隊

  • rPush:把值插到列表的右邊(尾部)r = right
  • lPush:把值插到列表的左邊(頭部) l = left

2、出隊

  • rPop:移除列表的最後一個元素,返回值爲移除的元素。(右邊,尾部)
  • lPop:移出並獲取列表的第一個元素(左邊,頭部)

3、阻塞等待

如果在規定時間timeOut內,沒有數據,則會掛起等待。

(1)、 brPop

  • 命令移出並獲取列表的最後一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。
  • 假如在指定時間內沒有任何元素被彈出,則返回一個 nil 和等待時長。 反之,返回一個含有兩個元素的列表,第一個元素是被彈出元素所屬的 key ,第二個元素是被彈出元素的值。

(2)、blPop

  • 命令移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。
  • 如果列表爲空,返回一個 nil 。 否則,返回一個含有兩個元素的列表,第一個元素是被彈出元素所屬的 key ,第二個元素是被彈出元素的值。
  • 第一個參數是key值,第二個參數timeOut是等待的時間,如果指定的列表存在數據則返回第一個元素,否則等待timeOut秒後返回nil

二、思路

1、入隊出隊

  • 如果從左邊入隊(lPush),那麼出隊就是往右邊(rPop);
  • 如果從右邊入隊(rPush),那麼出隊就是往左邊( lPop);

2、實現

  • 代碼中入隊是正常的業務邏輯,出隊則是監聽一個死循環 while(true){ }方法,大致語法如下:
//從左邊(頭部取出數據)
public function redisOut(){
    while (true){
        //從右邊(rPush)入隊,左邊阻塞出隊
        $data = $this->redis->blPop('tianPeng', 10);
        if ($data){
            /*
            ...
            處理相關業務邏輯
            ...
             */
            var_dump($data);
            echo PHP_EOL;
        }else{
            echo '等待中...' . date('Y-m-d H:i:s') . PHP_EOL;
        }
    }
}
  • redisOut 打印大概是這個樣子
    在這裏插入圖片描述

3、問題

  • 出隊不能使用 rPop,lPop,因爲這兩個方法是個長連接,一直連着Redis,redis報錯如下:
[RedisException]
  通常每個套接字地址(協議/網絡地址/端口)只允許使用一次。
  • 那就使用 brPop,blPop

三、完整代碼

<?php

namespace app\index\controller;

use app\common\exception\BaseException;

class Tianpeng extends Base
{
    public $redis;
    public function __construct()
    {
        parent::__construct();
       $this->redis = self::getRedisConn();
    }

    public function getRedisConn(){
        try{
            $redis = new \Redis();  //直接用自己安裝的Redis擴展
            $connect = $redis->connect('127.0.0.1', '6379');    //主機、端口號
            $password = ''; //密碼
            $authConnect = true;
            if (!empty($password)) {
                $authConnect = $redis->auth($password);
            }
            $redis->select(1);   //存放的數據庫索引

            if(!$connect || !$authConnect){  # 因爲phpredis在連接失敗時,不會拋出異常,只返回false
                throw new BaseException('redis網絡錯誤請刷新!');
            }
            return $redis;
        }catch (\Exception $e){
            throw $e;
        }
    }

    //從右邊(尾部插入數據)
    public function redisIn(){
        for ($i = 1; $i <= 10; $i++){
            sleep(1);
            $date = date('Y-m-d H:i:s');
            echo $date . '<br>';
            //如果存入的數據是數組,就用json_encode轉爲字符串,再存
            //$data = json_encode($data, JSON_UNESCAPED_UNICODE);
            $this->redis->rPush('tianPeng', $date);
        }
    }

    //從左邊(頭部取出數據)
    public function redisOut(){
        while (true){
            //從右邊(rPush)入隊,左邊阻塞出隊
            $data = $this->redis->blPop('tianPeng', 10);
            if ($data){
                /*
                ...
                處理相關業務邏輯
                ...
                 */
                var_dump($data);
                echo PHP_EOL;
            }else{
                echo '等待中...' . date('Y-m-d H:i:s') . PHP_EOL;
            }
        }
    }
}

四、有話要說

  • 如有錯誤,望各位大佬批評指正,感激不盡 >_<
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章