关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言
上一期分享了一篇关于阿里巴巴JetCache
框架的文章,其中有一处小编表示疑惑。那就是线下测试时,多个节点更新缓存后,无法同步更新其他节点的本地缓存。结果还提了一个issues
:github.com/alibaba/jet...
小编越想越不对,拥有5.4k
star不可能有这样的Bug
。于是又查看了已经closed
的issues
,才发现有不少人遇到同样的问题,并且已经解决。
我们一起一探究竟!
02 缘起
因为本地测试,所以当时并没有选用所有配置,而是节选了其中的部分配置,其中最关键的一个属性如图:

正是为了解决二级缓存更新后同步其他JVM
的Local Cache。小编高高兴兴的去增加配配置:
properties
jetcache.remote.default.broadcastChannel=boot-cache
发现并没有生效,一通搜索下来才了解到@Cached
的一个属性必须添加,默认是关闭的,我们需要开启:
java
@Cached(syncLocal = true)

问题终于解决了。从注释里面可以知道是通过BroadcastManager
广播实现的,因为我用的是Jedis
客户端,所以直接看com.alicp.jetcache.redis.RedisBroadcastManager
即可。
03 源码追踪
com.alicp.jetcache.anno.aop.JetCacheInterceptor

从源码可以看到,缓存的更新和删除都会触发Redis的发布机制。
com.alicp.jetcache.redis.RedisBroadcastManager

截图上可以看到,直接调用了Jedis
客户端的发布的方法。
Redis的订阅是在创建com.alicp.jetcache.anno.method.CacheInvokeContext
时启用的,如图:

一步步跟踪就会发现,最终还是走到了com.alicp.jetcache.redis.RedisBroadcastManager#startSubscribe
方法:

继续看看订阅的消息最后怎么处理的:

从结果来看,缓存的更新和删除对于本地缓存的处理都是直接删除。
04 发布订阅Jedis版
我们也可以自己手动显示一下Redis
的发布订阅功能。
java
public class JedisTest {
static JedisCluster cluster = null;
/** 创建集群(也可以使用单机) */
@BeforeAll
static void before() {
Set<HostAndPort> sets = new HashSet<>();
sets.add(new HostAndPort("127.0.0.1", 7000));
sets.add(new HostAndPort("127.0.0.1", 7001));
sets.add(new HostAndPort("127.0.0.1", 7002));
cluster = new JedisCluster(sets);
}
/** 发布者 */
@Test
void test01() {
long publish = cluster.publish("jedis-test-channel", "总部发布了一则寻人启事");
System.out.println(publish);
}
/** 订阅者:由于使用测试用例,所以需要使用System.in.read()阻塞线程 */
@Test
void test02() throws IOException {
cluster.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("channel:" + channel + ", message" + message);
}
}, "jedis-test-channel");
System.in.read();
}
}
效果
可以看到test01
发布了消息,test02也订阅到了消息

05 Spring Boot的使用
我们使用的客户端是org.springframework.data.redis.core.StringRedisTemplate
5.1 Maven
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
5.2 配置
Spring Boot
的配置帮我们封装了很多可用的类,我们只要配置到Spring
容器中即可:
org.springframework.data.redis.listener.RedisMessageListenerContainer
org.springframework.data.redis.listener.adapter.MessageListenerAdapter
配置文件
properties
spring.redis.cluster.nodes[0]=127.0.0.1:7000
spring.redis.cluster.nodes[1]=127.0.0.1:7001
spring.redis.cluster.nodes[2]=127.0.0.1:7002
配置项
java
@Configuration
public class RedisConfig {
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new ChannelTopic("redis-publish"));
return container;
}
@Bean
public MessageListenerAdapter listenerAdapter(RedisMessageLister redisMessageLister) {
return new MessageListenerAdapter(redisMessageLister, "testMessage");
}
}
需要说明的是,这里的RedisMessageListenerContainer
和MessageListenerAdapter
必须配置。通过RedisMessageListenerContainer
添加MessageListenerAdapter
并指定广播的通道名称。
MessageListenerAdapter
构造的器如果使用:
java
public MessageListenerAdapter(Object delegate, String defaultListenerMethod) {
this(delegate);
setDefaultListenerMethod(defaultListenerMethod);
}
RedisMessageLister
类可以任意定义,并通过defaultListenerMethod
指定方法即可。
java
@Component
public class RedisMessageLister {
public void testMessage(String message) {
System.out.println("Received message: " + message);
}
}
如何使用下面的构造:
java
public MessageListenerAdapter(Object delegate) {
initDefaultStrategies();
setDelegate(delegate);
}
自定义的方法只能是handleMessage
,因为默认的就是handleMessage
。当然可以直接实现org.springframework.data.redis.connection.MessageListener
接口,无需关心方法名。
java
@Component
public class RedisMessageLister implements MessageListener{
@Override
public void onMessage(Message message, byte[] pattern) {
String channel = new String(message.getChannel());
String body = new String(message.getBody());
String patterns = new String(pattern);
System.out.println("收到的消息: " + body + " from channel: " + channel + ",patterns=" + patterns);
}
}
这是为什么?请看源码:

5.3 定义发布者
java
@Slf4j
@RestController
@RequestMapping("redis")
public class ReidsPushlishController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("message")
public String publish(String message) {
stringRedisTemplate.convertAndSend("redis-publish", message);
log.info("总部发布了一条消息: " + message);
return "消息发布成功!";
}
}
06 小结
Redis 发布订阅提供了一种轻量级的实时消息传递机制,在 Java 中可通过 Jedis 或 Spring Data Redis 快速集成。关键要认清其"即发即忘"的本质------适用于允许丢失消息的实时场景。对于需要持久化、事务支持、高可靠性的场景,建议结合 Redis Stream 或专业消息队列使用。