👀发布/订阅
-
Redis 支持发布/订阅模式,允许客户端订阅指定的频道并收到发送到这些频道的消息。
-
在 Spring Data Redis 中,你可以使用
RedisMessageListenerContainer
来处理消息的订阅和接收。
✌ 作用:
- Redis 的发布/订阅 (Pub/Sub) 是一个消息通信模式,允许客户端发布消息到一个频道并让其他客户端订阅这个频道,以接收其发布的消息。
✌ 使用场景:
- 实时消息通知:例如,通知用户新的聊天消息或系统通知。
- 事件触发:当某些事件在系统中发生时,通知其他部分或微服务。
- 日志和监控:集中处理系统的日志或监控信息。
✌ 优点:
- 实时性:消息传递几乎是实时的,适用于需要快速响应的应用。
- 简单和轻量级:与其他消息队列或代理相比,Redis Pub/Sub 更简单且开销较小。
- 灵活性:客户端可以随时订阅或取消订阅频道。
✌ 缺点:
- 不持久化:如果订阅者在消息发布时不在线,它将错过该消息。
- 不保证消息传递:没有确认机制来保证消息已被所有订阅者接收。
- 无负载均衡:所有订阅者都会收到所有消息,而不是像队列系统那样只有一个消费者处理消息。
✌ 示例代码:
✍1. 订阅频道:
首先,你需要定义一个消息监听器来处理收到的消息:
public class MessageSubscriber implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println("Received message: " + new String(message.getBody()));
}
}
然后,配置 RedisMessageListenerContainer
来监听频道:
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory connectionFactory;
@Bean
RedisMessageListenerContainer redisContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(new MessageSubscriber(), new PatternTopic("myChannel"));
return container;
}
}
✍2. 发布消息:
使用 RedisTemplate
发布消息到指定的频道:
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void publishMessage() {
redisTemplate.convertAndSend("myChannel", "Hello, Redis Pub/Sub!");
}
-
以上代码示例演示了如何在 Spring Boot 项目中使用 Spring Data Redis 进行消息的发布和订阅。当你调用
publishMessage()
方法时,所有订阅了myChannel
的客户端都会收到 “Hello, Redis Pub/Sub!” 消息。 -
发布/订阅模式在需要实时消息传递的应用中非常有用,但要确保你了解其限制,并根据应用的需求做出适当的选择。如果你需要更可靠、持久化或有负载均衡的消息传递,考虑使用其他消息队列解决方案,如 RabbitMQ 或 Kafka。
👀MessageListenerAdapter
MessageListenerAdapter
是Spring Framework中的一个类,用于将消息监听器(MessageListener)适配到不同类型的消息通道(如消息队列)。它的主要作用是将消息的处理逻辑与消息传递机制解耦,使得消息消费者可以专注于处理消息内容,而不需要关心消息的传递细节。
🎯作用:
- 将消息监听器适配到不同的消息通道,如JMS、RabbitMQ、Kafka等。
- 解耦消息消费者与底层消息传递机制,使得消息消费者只需专注于处理消息内容。
🎯使用场景:
- 在Spring应用中需要消费来自不同消息通道的消息时,可以使用
MessageListenerAdapter
来简化消息消费者的编写和配置。 - 当需要在同一个应用中处理多种类型的消息,每种消息类型有不同的处理方法时,可以使用适配器模式来统一处理。
🎯优点:
- 解耦消息消费者与消息传递机制,使得代码更具可维护性和灵活性。
- 支持多种消息通道和消息格式,适用于不同的场景。
- 简化消息消费者的编写,使得开发人员只需专注于业务逻辑而不用关心底层消息传递细节。
🎯缺点:
- 对于简单的消息处理场景,可能会引入一些额外的复杂性。
- 可能需要配置多个适配器来处理不同类型的消息,增加了配置的复杂性。
🎯示例代码:
假设你有一个Spring Boot应用,需要从RabbitMQ队列中消费消息,并根据消息的不同类型进行不同的处理。以下是一个简单的示例代码:
- 创建一个消息处理类,用于处理不同类型的消息:
import org.springframework.stereotype.Component;
@Component
public class MyMessageHandler {
public void handleMessage(String message) {
System.out.println("Received plain text message: " + message);
}
public void handleJsonMessage(MyJsonMessage jsonMessage) {
System.out.println("Received JSON message: " + jsonMessage);
}
}
- 配置消息监听适配器和消息监听容器:
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
@Configuration
public class RabbitMQConfig {
@Autowired
private MyMessageHandler messageHandler;
@Bean
public MessageListenerAdapter plainTextMessageListenerAdapter() {
MessageListenerAdapter adapter = new MessageListenerAdapter(messageHandler, "handleMessage");
return adapter;
}
@Bean
public MessageListenerAdapter jsonMessageListenerAdapter() {
MessageListenerAdapter adapter = new MessageListenerAdapter(messageHandler, "handleJsonMessage");
return adapter;
}
@Bean
public MessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("plainTextQueue", "jsonQueue");
container.setMessageListener(plainTextMessageListenerAdapter());
container.setMessageListener(jsonMessageListenerAdapter());
return container;
}
}
在上面的示例中,MessageListenerAdapter
被用来适配不同的消息处理方法,根据消息的类型进行分发。MyMessageHandler
类中的handleMessage
方法用于处理普通文本消息,而handleJsonMessage
方法用于处理JSON格式的消息。
请注意,上述示例中的代码仅用于演示概念,实际使用时需要根据项目的具体需求进行适当的修改和配置。
👀Redis Stream
Redis Stream 是 Redis 数据结构中的一种新型数据类型,用于处理实时数据流(如日志、事件等)。它提供了一个有序、持久化的数据流,能够存储、消费和处理大量的事件数据。在 Spring Boot 中,你可以使用 Spring Data Redis 来与 Redis Stream 进行交互。
🎯作用:
- 支持高吞吐量的实时事件数据流处理。
- 有序、持久化地存储和传输事件数据。
- 可以用于日志收集、事件驱动架构、消息队列等场景。
🎯使用场景:
- 日志收集和处理:将应用程序产生的日志消息以事件的形式写入 Redis Stream,然后异步消费并处理这些日志消息。
- 实时事件处理:使用 Redis Stream 来处理实时事件,如用户操作、系统事件等。
- 消息队列:作为简单的消息队列,将消息发送到 Stream 中,然后消费者可以按顺序处理这些消息。
🎯优点:
- 有序性:Redis Stream 保证了事件数据的有序性,适合处理需要按照顺序处理的场景。
- 持久化:事件数据持久化存储,即使在消费者处理失败后也不会丢失数据。
- 高吞吐量:适用于高并发、大规模事件数据处理。
- 消费者组:支持多个消费者以消费者组的方式订阅 Stream,实现负载均衡。
🎯缺点:
- 复杂性:相比传统的键值存储,使用 Stream 需要一定的学习和调整。
- 不适合低延迟场景:虽然 Redis 快速,但在某些实时性要求非常高的场景可能不太适合。
🎯示例代码:
假设你正在开发一个 Spring Boot 应用,用于处理用户注册事件,并将这些事件写入 Redis Stream,然后消费者从 Stream 中获取事件数据并进行处理。
- 添加 Spring Data Redis 依赖:
在 build.gradle
或 pom.xml
中添加 Spring Data Redis 依赖。
- 编写生产者代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.data.redis.stream.StreamMessage;
@Service
public class EventProducer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void produceEvent(String streamKey, String eventId, String eventData) {
StreamMessage<String, String> message = StreamMessage
.builder()
.id(eventId)
.value(eventData)
.build();
redisTemplate.opsForStream().add(streamKey, message);
}
}
- 编写消费者代码:
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;
@Component
public class EventConsumer implements StreamListener<String, MapRecord<String, String, String>> {
private final StreamOperations<String, String, String> streamOperations;
public EventConsumer(StreamOperations<String, String, String> streamOperations) {
this.streamOperations = streamOperations;
}
@Override
public void onMessage(MapRecord<String, String, String> message) {
String eventId = message.getId();
String eventData = message.getValue();
// Process event data here
System.out.println("Received event: " + eventData);
// Acknowledge the message
streamOperations.acknowledge("mystream", message);
}
}
- 配置消费者和生产者:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
@Configuration
public class RedisStreamConfig {
@Bean
public StreamListener<String, MapRecord<String, String, String>> eventConsumer() {
return new EventConsumer(redisTemplate().opsForStream());
}
@Bean
public RedisMessageListenerContainer messageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> options =
StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
.pollTimeout(Duration.ofMillis(1000))
.targetType(MapRecord.class)
.build();
return StreamMessageListenerContainer.create(redisConnectionFactory, options);
}
@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// Configure and return RedisConnectionFactory
}
}
在上面的示例中,我们创建了一个简单的事件生产者和一个事件消费者,并通过 Redis Stream 实现了事件的写入和消费。请注意,实际应用中还需要根据需求进行适当的配置和调整。
Redis Stream 是 Redis 5.0 引入的新数据结构,它提供了一个日志结构,其中数据仅被追加,这与Kafka和RabbitMQ这样的消息队列有些相似。它允许多个生产者向流中添加数据,并允许多个消费者消费流中的数据。
✌1. 追加
✍作用:
- 数据持久化:Redis Stream 存储数据的方式是追加到流中,这意味着数据不会被覆盖或删除。
- 实时性:可以立即将数据添加到流中,消费者可以实时获取。
✍使用场景:
- 日志记录:适用于系统或应用的日志记录。
- 消息队列:当需要多个消费者同时处理消息时。
✍优缺点:
优点:
- 高性能
- 支持多个消费者组
- 支持消息确认和重试
缺点:
- 学习曲线相对较高
- 与其他消息队列相比,功能可能不那么完善
✍示例代码:
// 引入依赖
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisStreamProducerService {
private final StringRedisTemplate redisTemplate;
public RedisStreamProducerService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 向 Redis Stream 添加数据
*
* @param streamKey 流的键
* @param data 要添加的数据
*/
public void produce(String streamKey, String data) {
redisTemplate.opsForStream().add(streamKey, "message", data);
}
}
✌2. 消费
✍作用:
- 数据处理:从 Redis Stream 中读取数据并进行处理。
- 实时性:可以实时或延迟消费。
✍使用场景:
- 日志分析:例如,分析系统日志并生成报告。
- 实时消息处理:例如,处理用户的实时请求。
✍优缺点:
优点:
- 高性能
- 支持消息确认和重试
缺点:
- 如果消费者处理速度不够快,可能会出现数据堆积
✍示例代码:
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisStreamConsumerService {
private final StringRedisTemplate redisTemplate;
public RedisStreamConsumerService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 从 Redis Stream 中消费数据
*
* @param streamKey 流的键
* @param count 要消费的数据数量
* @return 消费的数据
*/
public List<Map.Entry<Object, Object>> consume(String streamKey, long count) {
return redisTemplate.opsForStream().range(streamKey, 0, count - 1);
}
}
注意: 上述代码是一个简化版的示例,实际应用中可能需要更多的功能,例如消息确认、重试策略等。此外,为了完整使用 Redis Stream,还需要配置 Spring Boot 的 Redis 连接和其他相关设置。