springboot+redis集群 key过期监听 notify-keyspace-events 系统管理员 2022-10-07 11:45 328阅读 0赞 **目录** 1.应用场景 2.redis配置文件 3.命令实现 4.springboot实现 5.集群只收到单一节点key过期 6.后记 -------------------- # 1.应用场景 # 设备于平台之间有心跳,会每2分钟上报一次心跳数据,这样平台就能感知到设备在线。但是如果设备离线,就不会给平台发送心跳,这时,如何判断设备离线? 之前的一种解决方案:每2分钟跑一次定时器,判断数据库中在线的设备,与最近2分钟上报心跳的设备进行比较,如果数据库中是在线但是近2分钟没有收到心跳,说明设备离线。 缺点就是每2分钟需要跑定时器,而且需要存储最近2分钟的所有有心跳上报设备。 优点就是不需要借助第三方中间件,就是跑定时器。 那考虑到redis 的这一功能:能够开启监听key是否过期,过期时可回调通知进行业务处理。那这里的一个解决方案就是:在收到设备心跳时,将设备编号作为key存入redis,有效期是2分钟,如果之后在没到期之前又收到的此设备的心跳,就将有效期再延迟2分钟;如果之后没有收到心跳,在2分钟到的时候,就会触发key过期监听,进行业务处理,将设备置为离线。 缺点是每2分钟需要去更新key的有效期(不知道是否算作缺点) 优点是不需要跑定时器,借助第三方redis中间件的功能就可以解决 -------------------- 其他的很多场景,比如订票只有30分钟有效期,或者需要延迟执行的任务都可以采用这种解决方案 # 2.redis配置文件 # 查看Redis的配置文件的 event notification 部分 ############################# EVENT NOTIFICATION ############################## # Redis can notify Pub/Sub clients about events happening in the key space. # This feature is documented at http://redis.io/topics/notifications # # For instance if keyspace events notification is enabled, and a client # performs a DEL operation on key "foo" stored in the Database 0, two # messages will be published via Pub/Sub: # # PUBLISH __keyspace@0__:foo del # PUBLISH __keyevent@0__:del foo # # It is possible to select the events that Redis will notify among a set # of classes. Every class is identified by a single character: # # K Keyspace events, published with __keyspace@<db>__ prefix. # E Keyevent events, published with __keyevent@<db>__ prefix. # g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... # $ String commands # l List commands # s Set commands # h Hash commands # z Sorted set commands # x Expired events (events generated every time a key expires) # e Evicted events (events generated when a key is evicted for maxmemory) # A Alias for g$lshzxe, so that the "AKE" string means all the events. # # The "notify-keyspace-events" takes as argument a string that is composed # of zero or multiple characters. The empty string means that notifications # are disabled. # # Example: to enable list and generic events, from the point of view of the # event name, use: # # notify-keyspace-events Elg # # Example 2: to get the stream of the expired keys subscribing to channel # name __keyevent@0__:expired use: # # notify-keyspace-events Ex # # By default all notifications are disabled because most users don't need # this feature and the feature has some overhead. Note that if you don't # specify at least one of K or E, no events will be delivered. notify-keyspace-events "" 修改配置文件成: notify-keyspace-events Ex 因为我这里是正常运行中的redis,所以在登录redis-cli后执行: config set notify-keyspace-events Ex 表示当key过期的时候进行监听 # 3.命令实现 # 打开一个redis-cli 订阅PSUBSCRIBE \_\_keyevent@0\_\_:expired ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQyMDkyMDU_size_16_color_FFFFFF_t_70][] 再打开一个redis-cli 执行:set key value \[EX EXPIRES\] 再过期时间到的时候,前面监听的客户端就能收到信息了 ![20210617221353538.png][] # 4.springboot实现 # redis配置文件 主要是声明 RedisMessageListenerContainer ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQyMDkyMDU_size_16_color_FFFFFF_t_70 1][] 然后再配置一个监听器 @Slf4j @Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { log.info("RedisKeyExpirationListener on message"); String channel = new String(message.getChannel()); // __keyevent@*__:expired String pa = new String(pattern); // __keyevent@*__:expired String expiredKey = message.toString(); log.info("监听到过期key:{},{},{}",channel,pa , expiredKey); } } 这样启动后,在有key过期的时候,就会进入onMessage回调方法。 # 5.集群只收到单一节点key过期 # 上面在redis 单节点的情况下,运行良好,都能收到过期的key。 但是在集群环境下,比如是3主3从的集群,springboot 在配置链接了3个主节点时,在同一时间实际上是只链接了一个node 比如6个节点的端口分别是:7001,7002,7003,7004,7005,7006 前面3个是主节点,后面3个是从节点。如果7001节点过期收到key,但是7002 和7003节点的key过期可能就收不到了 > 也就是说键空间通知是指定node的(自身),不像常规的pub/sub是广播到所有节点的,所以我们需要连接集群中所有的可用节点去获取键空间通知 > 换句话说,Redis Cluster集群中key采用的是分片存储,不同的key通过哈希计算放到不同的slot槽中,即可能是不同的node节点,而keyspace notification只在自己所在的node上发布,并没有发布到集群当中,我们客户端订阅监听的时候只监听随机的node(即每次建立连接的node是随机的),那么就有可能有些key过期没有被监听到,这就导致说我们收不到这个过期事件。 > 即集群本身的pub/sub是节点之间交叉广播的,但是键空间通知只支持本地 > 也就是说因集群模式点对点之间网络带宽的压力,不考虑将键空间通知加入到集群广播中来,更建议是客户端直接连接节点获取键空间通知,但是有个问题就是需要客户端随时检查集群配置,以获取新加入的master节点 上面的几段都来自下面第三篇参考文章[【3】][3] # 6.后记 # 【参考】: [redis是怎么监控失效的key][redis_key] [redis key过期监听][redis key] [【redis cluster】redis 集群模式下,key过期收不到的问题处理][3] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQyMDkyMDU_size_16_color_FFFFFF_t_70]: /images/20221005/0ae58b1ad7bc422bb58936658b9c578e.png [20210617221353538.png]: /images/20221005/a42a4d33f10c4a13b3fb95376e637c77.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQyMDkyMDU_size_16_color_FFFFFF_t_70 1]: /images/20221005/2848787dcde84231a52b48c3446c5802.png [3]: https://blog.csdn.net/arnolan/article/details/102065525 [redis_key]: https://www.php.cn/redis/421934.html [redis key]: https://www.cnblogs.com/yangyongjie/p/14399707.html
还没有评论,来说两句吧...