问题一:启用监听收不到过时时间消息,缘由是未开启配置
解决办法是 在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.