node实现redis键空间通知和分布式锁

2,359 阅读3分钟

业务背景

在线聊天页面,当用户离开一定时间后,会触发超时结束会话的一个逻辑。

redis键空间通知

键空间通知, redis 2.8版本才支持的特性,当redis的key被删除或者过期后,会触发两种不同类型的事件通知

__keyspace@0__:mykey del
__keyevent@0__:del mykey

订阅第一个频道 keyspace@0:mykey 可以接收 0 号数据库中所有修改键 mykey 的事件, 而订阅第二个频道 keyevent@0:del 则可以接收 0 号数据库中所有执行 del 命令的键。

开启键空间通知功能需要消耗一些 CPU , 所以在默认配置下, 该功能处于关闭状态

需要在redis.conf中开启该配置或者直接使用CONFIG SET命令

搬运一张图:

如果要开启键过期事件通知,可以在配置中写入notify-keyspace-events EX

至此,redis的配置改完。

然后在eggjs中,增加如下配置

    const { redisNodes } = this.app.config;
redisNodes.forEach(node => {
  const sub = new Redis(node);
  // 保证连接成功后再去监听
  sub.once('connect', () => {
    // 只订阅过期事件
    sub.subscribe('__keyevent@0__:expired');
    // 订阅消息回调
    sub.on('message', async (channel, message) => {
    });

  });
});

因为公司的redis服务是3主3从的架构,所以这里的redis是多台服务器,需要遍历去监听,因为这个订阅事件的回调,只会回调给当前监听的redis机器,集群模式下无法监听到,所以,并不能直接在eggjs中直接使用配置好的this.app.redis这个变量去监听服务。

分布式锁

上面实现了redis键空间通知,但是第二个问题随之而来,node服务在两台不同的机子上,redis服务也在两台不同的机子上,因为我们开启监听是每台node服务都会去开启不同的redis服务监听,所以实际上会有一个资源抢占的现象,因为回调会同时会被触发,此时就需要用到分布式锁的概念了。

redis的命令是原子性的

所谓原子性是说是:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成

增加如下代码:

  const sub = new Redis(node);
      // 保证连接成功后再去监听
      sub.once('connect', () => {
        // 只订阅过期事件
        sub.subscribe('__keyevent@0__:expired');
        // 订阅消息回调
        sub.on('message', async (channel, message) => {
          console.log('channel', channel, 'message:', message);
          // 如果不是指定的事件,直接返回
          if (!message.startsWith('chat.leave')) {
            return;
          }
          // 实现分布式锁,因为node服务在两台机器上110,111 两台机器都会监听两个redis服务器比如,95和96
          // redis在回调的时候,由于两台node服务器都监听这95,96端口,所以会去抢占谁去获得这个资源的执行权
          const data = await this.app.redis.set(`__@NX_${message}`, '1', 'NX', 'EX', 10);
          // 正是因为同时做了set操作,这个操作只会让一台机器成功,其它的机器设置data都会报null
          if (data) {
            // 3. 业务处理
      }

    });

  });

注释写得比较清楚,主要是在set一个值的时候,redis不会让同一个key同时设置两次,主要是NX这个命令起作用,当一台机器抢占到执行权时,另外一台机子set这个方法,就会返回null。

至此,就简单的实现了分布式锁的功能了。

其实关于键空间通知还有其它一些场景,比如:

  1. 订单超时自动取消
  2. 用户超过7天不登录短信提醒
  3. ...

本文使用 mdnice 排版