日常八股文记录

Redis Streams是一个仅追加(Append-Only)的持久化消息日志。它不是一个普通的 List(列表)或 Pub/Sub(发布订阅)每条消息进来都会自动生成一个全局唯一的 ID(时间戳+序号),并且消息会持久化在内存(及 RDB/AOF)中,不会因为消费者离线而丢失。

会有一个消费者组,来对Streams进行消费。同一条消息只会被组内的某一个消费者拿到(实现负载均衡)。

// ============================================================ // Redis 配置 // ============================================================ struct RedisConfig { std::string host = "127.0.0.1"; int port = 6379; std::string password = ""; int db = 0; // Streams 配置 std::string streamKey = "device:commands"; std::string consumerGroup = "gateway-group"; std::string consumerName = "gateway-1"; // 启动时动态覆盖 // 连接池配置 int poolSize = 4; // 连接池大小 int connTimeoutMs = 3000; // 连接超时 int cmdTimeoutMs = 5000; // 命令超时 };

除此之外,还有一个极其重要的隐藏概念:PEL(Pending Entries List,待处理列表)。当消费者取走一条消息但还没回复XACK(确认)时,这条消息就挂在 PEL 里。这是 Redis Streams保证消息不丢失的基石。

可能问题:配置里有cmdTimeoutMs = 5000。如果你为了实时性,采用XREADGROUP BLOCK 0(永久阻塞等待新消息),该命令在收到下一条消息前,会一直阻塞住这条 TCP 连接。如果客户端的底层cmdTimeoutMs设置为 5 秒,那么每隔 5 秒这个阻塞命令就会因超时而断开,网关就会频繁重连,产生大量XGROUP重入日志。
解决方案:对于阻塞读命令,必须单独设置为无限超时(-1),或者采用短轮询(如BLOCK 1000超时重试)。

consumerName绝对不能重复

PEL 无限膨胀
如果网关处理设备命令时异常崩溃,没有来得及执行XACK,这些消息就会永远卡在 PEL 里。随着时间推移,PEL 会越来越大,导致XPENDING命令变慢,甚至撑爆 Redis 内存。
补救措施:启动一个后台定时任务,扫描XPENDING超时(如处理超过 30 秒)的消息,执行XCLAIM(认领)并重新处理,或者直接XACK并记录失败日志。

在分布式中,redis的数据库一共16个,但是只能使用db=0的那一个,暂时还不支持