Redis 发布订阅架构
Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel
,这里的Channel
类似于Kafka
中的topic
的概念。
发布者和订阅者都是Redis
客户端,Channel
则为Redis
服务器端,可以理解为一种特殊的数据存储结构。发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。
Redis
的这种发布订阅机制与基于主题的发布订阅类似,Channel
相当于主题。
使用Java Lettuce
客户端实现Redis发布订阅
发布者
消息发布者的代码很简单,就定义一个普通的方法,指定要发布到的channel和要发布的消息即可。
package it.aspirin.learnredis.pub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* The type Publish service.
*/
@Service
public class PublishService {
private final static Logger log = LoggerFactory.getLogger(PublishService.class);
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* Publish.
*
* @param topicName the topic name
* @param message the message
*/
public void publish(String topicName, String message) {
stringRedisTemplate.convertAndSend(topicName, message);
}
}
订阅者
消息订阅者的代码同样简单。方法传入订阅到的消息,然后进行处理即可。注意这里并没有指定订阅者订阅的通道是什么。
package it.aspirin.learnredis.listener;
import org.springframework.stereotype.Service;
/**
* The type Test listener.
*/
@Service
public class TestListener {
/**
* Receive message.
*
* @param msg the msg
*/
public void receiveMessage(String msg){
System.out.println(" subscribe msg = " + msg);
}
/**
* Receive message 2.
*
* @param msg the msg
*/
public void receiveMessage2(String msg){
System.out.println(" subscribe msg2 = " + msg);
}
}
配置
在订阅者的代码中并没有指定要订阅的channel。那必定在其他地方指定了,否则订阅者就不知道要消费哪个channel的数据了。
/**
* Container redis message listener container.
*
* @param connectionFactory the connection factory
* @param listenerAdapter1 the listener adapter 1
* @param listenerAdapter the listener adapter
* @return the redis message listener container
*/
@Bean
public RedisMessageListenerContainer container(LettuceConnectionFactory connectionFactory,
//这里可以指定多个MessageListenerAdapter,MessageListenerAdapter名字要与下面定义的bean的方法名字一致,否则会注入不进来
MessageListenerAdapter listenerAdapter1,
MessageListenerAdapter listenerAdapter){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//这里将channel的订阅者添加到container中,并指定要消费的channel
container.addMessageListener(listenerAdapter1,new PatternTopic("TEST_TOPIC"));
container.addMessageListener(listenerAdapter,new PatternTopic("TEST_TOPIC"));
return container;
}
/**
* 绑定消息监听者和接收监听的方法,必须要注入这个监听器,不然会报错
* 这里的listenerAdapter1要与上面container中定义的名字一致
* @param sub the sub
* @return the message listener adapter
*/
@Bean
public MessageListenerAdapter listenerAdapter1(TestListener sub){
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用TestListener中的“receiveMessage”方法
return new MessageListenerAdapter(sub,"receiveMessage");
}
/**
* Listener adapter message listener adapter.
* 这里的listenerAdapter要与上面container中定义的名字一致
* @param sub the sub
* @return the message listener adapter
*/
@Bean
public MessageListenerAdapter listenerAdapter(TestListener sub){
//这里通过反射的放射调用TestListener中的receiveMessage2方法
return new MessageListenerAdapter(sub,"receiveMessage2");
}
Redis发布订阅的缺点
- 消息无法持久化,存在丢失风险,即消息一经发布,即使没有任何订阅方处理,该条消息就会丢失
- 没有类似ACK的机制,即发布方不会确保订阅方成功接收
- 广播机制,下游消费能力取决于消费方本身。广播机制无法通过添加多个消费方增强消费能力,因为这和发布/订阅模型本身的目的是不符的.广播机制的目的是一个一个发布者被多个订阅进行不同的处理
以上三个缺点可以说都是非常致命的,因此在考虑使用Redis发布订阅功能的时候一定要非常慎重。
Redis发布/订阅应用场景
由于Redis发布/订阅模型存在的缺陷,所以使用前需要考虑如下几点:
- 对于消息处理可靠性要求不强
- 消费能力无需通过增加消费方进行增强