Springboot-Redis - 10.Redis 消息 (Pub/Sub)

👀发布/订阅

  • 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队列中消费消息,并根据消息的不同类型进行不同的处理。以下是一个简单的示例代码:

  1. 创建一个消息处理类,用于处理不同类型的消息:
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);
    }
}
  1. 配置消息监听适配器和消息监听容器:
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 中获取事件数据并进行处理。

  1. 添加 Spring Data Redis 依赖:

build.gradlepom.xml 中添加 Spring Data Redis 依赖。

  1. 编写生产者代码:
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);
    }
}
  1. 编写消费者代码:
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);
    }
}
  1. 配置消费者和生产者:
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 连接和其他相关设置。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个简单的Spring Boot Redis Pub/Sub示例代码: 首先需要在pom.xml中添加redis和spring-boot-starter-data-redis依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.6.0</version> </dependency> ``` 然后编写一个Redis消息监听器: ```java @Component public class RedisMessageListener { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private MessageListenerAdapter messageListenerAdapter; @PostConstruct public void init() { new Thread(() -> { Jedis jedis = new Jedis("localhost"); jedis.subscribe(messageListenerAdapter, "test-channel"); }).start(); } @Bean public MessageListenerAdapter messageListenerAdapter() { return new MessageListenerAdapter(new RedisMessageSubscriber()); } public class RedisMessageSubscriber { public void handleMessage(String message) { System.out.println("Received message: " + message); } } } ``` 然后可以在其他地方发布消息Redis中: ```java @RestController public class RedisController { @Autowired private StringRedisTemplate stringRedisTemplate; @GetMapping("/publish") public String publish() { stringRedisTemplate.convertAndSend("test-channel", "Hello, Redis!"); return "Message sent"; } } ``` 这样,当调用/publish接口时,就会向Redis的test-channel频道发布一条消息RedisMessageListener中的RedisMessageSubscriber就会收到该消息并进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yueerba126

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值