springboot+redis集群 key过期监听 notify-keyspace-events

目录

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

springboot+redis集群 key过期监听 notify-keyspace-events

再打开一个redis-cli

执行:set key value [EX EXPIRES]

再过期时间到的时候,前面监听的客户端就能收到信息了

springboot+redis集群 key过期监听 notify-keyspace-events

4.springboot实现

redis配置文件

主要是声明 RedisMessageListenerContainer

springboot+redis集群 key过期监听 notify-keyspace-events

然后再配置一个监听器

@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节点

上面的几段都来自第三篇参考文章

6.后记

 

【参考】:

redis是怎么监控失效的key

redis key过期监听

【redis cluster】redis 集群模式下,key过期收不到的问题处理

 

上一篇:pointer-events: none


下一篇:web前端入门到实战:几种HTML标签伪元素绑定事件的方式