laravel使用redis監聽在內部再次使用redis遇到的問題

問題一:啓用監聽收不到過時時間消息,緣由是未開啓配置
解決辦法是 在redis配置文件內開啓 notify-keyspace-events Ex或者在redis命令行 redis-cli 使用命令:php

config set notify-keyspace-events Ex

問題二:PredisConnectionConnectionException : Error while reading line from the serverredis

緣由是Redis默認連接時間未60秒,在database.php設置read_write_timeout爲0便可。網絡

"read_write_timeout"=>0

問題三:ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context
這個是由於一個Redis連接使用監聽時,沒法使用其餘命令。須要從新創建一個連接。期初我使用 new \Predis\Client(),一直報錯,我也不知道爲啥。而後我想到了使用集羣,使用相同配置。將監聽事件設置爲單獨實例。具體操做以下:數據結構

//datebase.php配置頁面
'redis' => [
        'client' => 'predis',
        'default' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
            "queue" => '{default}',//queue站點默認走的redis
        ],
        'publisher' => [ //redis 訂閱監聽
                    'host' => env('REDIS_HOST', '127.0.0.1'),
                    'password' => env('REDIS_PASSWORD', null),
                    'port' => env('REDIS_PORT', 6379),
                    'database' => 0,
                    "read_write_timeout"=>0,//長鏈接不要斷
                ],
           ]
//監聽頁面
//__keyevent@*__:expired監聽過時消息
$redis=Redis::connection('publisher');//建立新的實例
$redis->psubscribe(['__keyevent@*__:expired'], function ($message, $channel) {
   echo $message;
   Redis::set('aa','123');//這樣就不會報錯了。這裏使用的是default的,是兩個redis連接。
});

 

Pub/Sub:
"發佈/訂閱"在redis中,被設計的很是輕量級和簡潔,它作到了消息的「發佈」和「訂閱」的
基本能力;可是還沒有提供關於消息的持久化等各類企業級的特性。

一個Redis client發佈消息,其餘多個redis client訂閱消息,發佈的消息「即發即失」,redis
不會持久保存發佈的消息;消息訂閱者也將只能獲得訂閱以後的消息,通道中此前的消息將無
從得到。socket

消息發佈者,即publish客戶端,無需獨佔連接,你能夠在publish消息的同時,使用同一個redis-client連接進行其餘操做(例如:INCR等)
消息訂閱者,即subscribe客戶端,須要獨佔連接,即進行subscribe期間,redis-client沒法穿插其餘操做,
此時client以阻塞的方式等待「publish端」的消息;所以這裏subscribe端須要使用單獨的連接,甚至須要在額外的線程中使用。
Tcp默認鏈接時間固定,若是在這時間內sub端沒有接收到pub端消息,或pub端沒有消息產生,sub端的鏈接都會被強制回收,
這裏就須要使用特殊手段解決,用定時器來模擬pub和sub之間的保活機制,定時器時間不能超過TCP最大鏈接時間,具體根據機器環境來定;ui

一旦subscribe端斷開連接,將會失去部分消息,即連接失效期間的消息將會丟失,因此這裏就須要考慮到藉助redis的list來持久化;this

若是你很是關注每一個消息,那麼你應該基於Redis作一些額外的補充工做,若是你指望訂閱是持久的,那麼以下的設計思路能夠借鑑:lua

1) subscribe端:
首先向一個Set集合中增長「訂閱者ID」, 此Set集合保存了「活躍訂閱」者,
訂閱者ID標記每一個惟一的訂閱者,此Set爲 "活躍訂閱者集合"spa

2) subcribe端開啓訂閱操做,並基於Redis建立一個以 "訂閱者ID" 爲KEY的LIST數據結構,
此LIST中存儲了全部的還沒有消費的消息,此List稱爲 "訂閱者消息隊列"

3) publish端:
每發佈一條消息以後,publish端都須要遍歷 "活躍訂閱者集合",並依次
向每一個 "訂閱者消息隊列" 尾部追加這次發佈的消息.

4) 到此爲止,咱們能夠基本保證,發佈的每一條消息,都會持久保存在每一個 "訂閱者消息隊列" 中.

5) subscribe端,每收到一個訂閱消息,在消費以後,必須刪除本身的 "訂閱者消息隊列" 頭部的一條記錄.

6) subscribe端啓動時,若是發現本身的 "訂閱者消息隊列" 有殘存記錄, 那麼將會首先消費這些記錄,而後再去訂閱.命令行

以上方法能夠保證成功到達的消息必消費不丟失;
但仍是會存在ngx業務機方自丟失數據問題,也就是ngx業務機自身問題或網絡問題致使ngx業務機發布的消息沒有送達redis機器;
更完善的確認機制才能完全解決上述存在問題;

注意,在實際ngx_lua_redis應用中,redis單個客戶端訂閱模式下僅能使用有限的幾個命令,不能使用其它結構命令,如lpop,rpush等;由於 publish是普通的request/response模式, 但subscribe不是,不然會報錯:      ERR only (P)SUBSCRIBE \/ (P)UNSUBSCRIBE \/ PING \/ QUIT allowed in this cont 關於這點如下是官網通常解釋: You are required to use two connections for pub and sub. A subscriber connection cannot issue any commands other than subscribe, psubscribe, unsubscribe, punsubscribe (although @Antirez has hinted of a subscriber-safe ping in the future). If you try to do anything else, redis tells you: -ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context(note that you can't test this with redis-cli, since that understands the protocol well enough to prevent youfrom issuing commands once you have subscribed - but any other basic socket tool should work fine) This is because subscriber connections work very differently - rather than working on a request/response basis,incoming messages can now come in at any time, unsolicited.publish is a regular request/response command, so must be sent on a regular connection, not a subscriber connection.

相關文章
相關標籤/搜索