Pub/Sub 入门
PUBLISH/SUBSCRIBE、与消息队列区别。
发布订阅:跨实例的消息广播
这一课你将能做到
- 用两个终端完成 SUBSCRIBE/PUBLISH 全流程,读懂 PUBLISH 返回的「订阅者数量」意味着什么
- 说清 Pub/Sub「不持久、不补投、无 ACK」的三大限制,判断什么场景能用、什么场景必须上 MQ
- 用 Spring 的 RedisMessageListenerContainer 搭一个跨实例「缓存失效广播」骨架,对应本站 Caffeine 多实例不同步问题
为什么要学
第 6 课留了一个尾巴:多实例各自持有 Caffeine 本地缓存,A 实例更新数据清了自己的缓存,B 实例还拿着最长 30 秒旧数据。如果要求更快一致,就需要A 实例更新后立刻通知所有实例清缓存——广播。
Redis 的 Pub/Sub 就是现成的广播电台:发布者喊一嗓子,所有订阅该频道的实例同时收到。它是 Redis 里最轻的「消息」能力——但轻是有代价的:不落盘、离线不补。这一课教你用对它,也教你什么时候不能用。
先建立图像
┌─ 实例A(SUBSCRIBE coderoadmap:cache-evict)
PUBLISH ──────┼─ 实例B(SUBSCRIBE coderoadmap:cache-evict)
(fire) └─ 实例C(SUBSCRIBE coderoadmap:cache-evict)
频道 = 广播电台频率;订阅 = 开着收音机
特点:没人开机时喊了也白喊(消息即焚);收音机只收「开机后」的广播
跟着做
步骤 1:双终端体验收发
终端 1(订阅者):
docker exec -it redis-dev redis-cli -a devpass123
SUBSCRIBE demo:chat
# 进入长驻等待,收到消息会打印 message 频道 内容
终端 2(发布者):
docker exec redis-dev redis-cli -a devpass123
PUBLISH demo:chat "hello-subscribers"
# (integer) 1 ← 返回值:这个频道当前有 1 个订阅者收到了
切回终端 1:已实时收到。PUBLISH 返回值就是「送达数」——0 表示没人订阅,消息已丢且不会有任何补偿。
步骤 2:PSUBSCRIBE 通配订阅
终端 1 重新订阅:
PSUBSCRIBE coderoadmap:cache-evict:*
终端 2:
PUBLISH coderoadmap:cache-evict:tracks "coderoadmap:tracks:list"
PUBLISH coderoadmap:cache-evict:articles "coderoadmap:articles:list"
通配符让一个监听器接收一类频道的消息——按缓存分组广播,语义清晰。
步骤 3:亲手验证「离线即丢」
# 确保所有订阅终端都 UNSUBSCRIBE 后:
PUBLISH demo:chat "nobody-listening"
# (integer) 0 ← 0 个订阅者收到
# 现在再开一个新的订阅终端 → 永远收不到刚才那条
步骤 4:Spring 侧监听骨架
第 6 课的场景落到代码(骨架,理解三件套结构):
@Configuration
public class CacheEvictPubSubConfig {
@Bean
RedisMessageListenerContainer cacheEvictContainer(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener((Message message, byte[] pattern) -> {
String body = new String(message.getBody(), StandardCharsets.UTF_8);
// body = 要失效的 key,如 "coderoadmap:tracks:list"
// 按前缀清对应 Caffeine cache:cacheManager.getCache("tracks").clear()
}, new PatternTopic("coderoadmap:cache-evict:*"));
return container;
}
}