zoukankan      html  css  js  c++  java
  • laravel使用redis监听在内部再次使用redis遇到的问题

    问题一:启用监听收不到过期时间消息,原因是未开启配置
    解决办法是 在redis配置文件内开启 notify-keyspace-events Ex或者在redis命令行 redis-cli 使用命令:

    config set notify-keyspace-events Ex

    问题二:PredisConnectionConnectionException : Error while reading line from the server

    原因是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 PredisClient(),一直报错,我也不知道为啥。然后我想到了使用集群,使用相同配置。将监听事件设置为单独实例。具体操作如下:

    //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
    不会持久保存发布的消息;消息订阅者也将只能得到订阅之后的消息,通道中此前的消息将无
    从获得。

    消息发布者,即publish客户端,无需独占链接,你可以在publish消息的同时,使用同一个redis-client链接进行其他操作(例如:INCR等)
    消息订阅者,即subscribe客户端,需要独占链接,即进行subscribe期间,redis-client无法穿插其他操作,
    此时client以阻塞的方式等待“publish端”的消息;因此这里subscribe端需要使用单独的链接,甚至需要在额外的线程中使用。
    Tcp默认连接时间固定,如果在这时间内sub端没有接收到pub端消息,或pub端没有消息产生,sub端的连接都会被强制回收,
    这里就需要使用特殊手段解决,用定时器来模拟pub和sub之间的保活机制,定时器时间不能超过TCP最大连接时间,具体根据机器环境来定;

    一旦subscribe端断开链接,将会失去部分消息,即链接失效期间的消息将会丢失,所以这里就需要考虑到借助redis的list来持久化;

    如果你非常关注每个消息,那么你应该基于Redis做一些额外的补充工作,如果你期望订阅是持久的,那么如下的设计思路可以借鉴:

    1) subscribe端:
    首先向一个Set集合中增加“订阅者ID”, 此Set集合保存了“活跃订阅”者,
    订阅者ID标记每个唯一的订阅者,此Set为 "活跃订阅者集合"

    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 you
    from 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.

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 奇偶判断
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Qt: 访问容器(三种方法,加上for循环就四种了)good
  • 原文地址:https://www.cnblogs.com/matengfei123/p/12293124.html
Copyright © 2011-2022 走看看