数据库

Redis的订阅模式

以下是一个 Node.js 监听 Redis key 变化的完整示例,包含详细的配置和实现步骤:

一、环境准备


  • 启用 Redis 键空间通知

编辑 redis.conf 并添加:

notify-keyspace-events "KEA"  # 启用所有键空间和键事件通知

重启 Redis 或使用命令临时启用:

redis-cli config set notify-keyspace-events "KEA"

安装依赖

npm install ioredis

二、Node.js 监听脚本


const Redis = require('ioredis');

// 配置 Redis 连接
const redis = new Redis({
  host: 'localhost',      // Redis 服务器地址
  port: 6379,             // Redis 服务器端口
  password: 'your-password' // 如果有密码
});

// 创建专用的订阅客户端(建议与主客户端分离)
const subscriber = new Redis({
  host: 'localhost',
  port: 6379,
  password: 'your-password'
});

// 订阅所有 key 变化事件
async function subscribeToKeyEvents() {
  try {
    // 订阅键空间事件(格式:__keyspace@<db>__:<key>)
    await subscriber.subscribe('__keyspace@0__:*');
    
    // 订阅键事件事件(格式:__keyevent@<db>__:<event>)
    await subscriber.subscribe('__keyevent@0__:set');
    await subscriber.subscribe('__keyevent@0__:del');
    
    console.log('已成功订阅 Redis key 变化事件');
  } catch (error) {
    console.error('订阅失败:', error);
  }
}

// 处理接收到的事件
subscriber.on('message', (channel, message) => {
  if (channel.startsWith('__keyspace@')) {
    // 键空间事件:message 是事件类型(如 set、del)
    const key = channel.split(':')[1];
    console.log(`Key ${key} 发生变化: ${message}`);
    handleKeyChange(key, message);
  } else if (channel.startsWith('__keyevent@')) {
    // 键事件事件:message 是 key 名称
    const event = channel.split(':')[1];
    console.log(`事件 ${event} 发生在 Key: ${message}`);
    handleKeyEvent(message, event);
  }
});

// 自定义处理函数 - 键变化
function handleKeyChange(key, event) {
  switch (event) {
    case 'set':
      // 处理 key 设置事件
      redis.get(key).then(value => {
        console.log(`Key ${key} 的新值是: ${value}`);
      });
      break;
    case 'del':
      // 处理 key 删除事件
      console.log(`Key ${key} 已被删除`);
      break;
    case 'expire':
      // 处理 key 过期事件
      console.log(`Key ${key} 已过期`);
      break;
    default:
      console.log(`Key ${key} 的其他事件: ${event}`);
  }
}

// 自定义处理函数 - 事件发生
function handleKeyEvent(key, event) {
  console.log(`事件 ${event} 影响了 Key: ${key}`);
  // 根据事件类型执行不同逻辑
}

// 启动订阅
subscribeToKeyEvents();

// 优雅退出
process.on('SIGINT', () => {
  subscriber.quit();
  redis.quit();
  console.log('已断开 Redis 连接');
  process.exit();
});

三、测试示例


运行脚本后,在 Redis 客户端执行以下命令测试:

# 设置 key
redis-cli set user:1 "John Doe"

# 删除 key
redis-cli del user:1

# 设置带过期时间的 key
redis-cli setex temp:key 60 "value"

Node.js 脚本将输出:

Key user:1 发生变化: set
Key user:1 的新值是: John Doe
事件 set 发生在 Key: user:1
Key user:1 发生变化: del
Key user:1 已被删除
事件 del 发生在 Key: user:1
Key temp:key 发生变化: set
Key temp:key 的新值是: value
事件 set 发生在 Key: temp:key
...60秒后...
Key temp:key 发生变化: expire
Key temp:key 已过期
事件 expire 发生在 Key: temp:key

四、关键配置说明


  1. Redis 事件类型
    • KEA:启用所有键空间和键事件(生产环境建议按需启用,如 Ex 仅监听过期事件)
    • 常用事件:setdelexpirerenamehset 等
  2. 订阅模式
    • __keyspace@<db>__:<key>:监听特定 key 的所有事件
    • __keyspace@<db>__:*:监听所有 key 的所有事件
    • __keyevent@<db>__:<event>:监听特定事件的所有 key
  3. 多数据库支持
    • 将 0 替换为实际数据库编号(如 __keyspace@1__:*

五、生产环境注意事项


  1. 性能影响
    • 大量事件可能影响 Redis 性能,建议仅监听必要事件。
  2. 可靠性保障
    • 使用 ioredis 的自动重连机制(默认开启)
    • 添加事件处理的重试和幂等性
  3. 安全配置
    • 使用 Redis 密码和 TLS 加密
    • 通过 max_connections 限制连接数
  4. 监控与告警
    • 监控 Redis 命令耗时和 CPU 使用率
    • 设置事件处理超时告警