广告
返回顶部
首页 > 资讯 > 后端开发 > Python >springboot整合redis之消息队列
  • 535
分享到

springboot整合redis之消息队列

2024-04-02 19:04:59 535人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、项目准备二、配置类三、Redis中list数据类型定时器监听队列运行即监控队列四、发布/订阅模式五、ZSet实现延迟队列一、项目准备 依赖 <!-- R

一、项目准备

依赖

        <!-- RedisTemplate -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- Redis-Jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

application.yaml配置文件

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    timeout: 4000
    jedis:
      pool:
        max-wait: -1
        max-active: -1
        max-idle: 20
        min-idle: 10

二、配置类

public class ObjectMapperConfig {

    public static final ObjectMapper objectMapper;
    private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";

    static {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        javaTimeModule.aDDDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        objectMapper = new ObjectMapper()
                // 转换为格式化的JSON(控制台打印时,自动格式化规范)
                //.enable(SerializationFeature.INDENT_OUTPUT)
                // Include.ALWAYS  是序列化对像所有属性(默认)
                // Include.NON_NULL 只有不为null的字段才被序列化,属性为NULL 不序列化
                // Include.NON_EMPTY 如果为null或者 空字符串和空集合都不会被序列化
                // Include.NON_DEFAULT 属性为默认值不序列化
                .setSerializationInclusion(jsonInclude.Include.NON_NULL)
                // 如果是空对象的时候,不抛异常
                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                // 反序列化的时候如果多了其他属性,不抛出异常
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // 取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .setDateFORMat(new SimpleDateFormat(PATTERN))
                // 对LocalDateTime序列化跟反序列化
                .reGISterModule(javaTimeModule)

                .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
                // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
                .enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
        ;
    }

    static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString(value.format(DateTimeFormatter.ofPattern(PATTERN)));
        }
    }

    static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
            return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern(PATTERN));
        }
    }

}
@Configuration
public class RedisConfig {

    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用jdk的序列化方式)
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jacksonSerializer.setObjectMapper(ObjectMapperConfig.objectMapper);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 使用StringRedisSerializer来序列化和反序列化redis的key,value采用json序列化
        template.seTKEySerializer(stringRedisSerializer);
        template.setValueSerializer(jacksonSerializer);

        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jacksonSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

三、redis中list数据类型

在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部和尾部添加新的元素

优势:

  • 顺序排序,保证先进先出
  • 队列为空时,自动从Redis数据库删除
  • 在队列的两头插入或删除元素,效率极高,即使队列中元素达到百万级
  • List中可以包含的最大元素数量是4294967295

定时器监听队列

生产者

@Slf4j
@Component
public class MessageProducer {

    public static final String MESSAGE_KEY = "message:queue";

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    public void lPush() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Long size = redisTemplate.opsForList().leftPush(MESSAGE_KEY, Thread.currentThread().getName() + ":hello world");
                log.info(Thread.currentThread().getName() + ":put message size = " + size);
            }).start();
        }
    }
}

消费者:消费消息,定时器以达到监听队列功能

@Slf4j
@Component
@EnableScheduling
public class MessageConsumer {

    public static final String MESSAGE_KEY = "message:queue";

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Scheduled(initialDelay = 5 * 1000, fixedRate = 2 * 1000)
    public void rPop() {
        String message = (String) redisTemplate.opsForList().rightPop(MESSAGE_KEY);
        log.info(message);
    }
}
@RestController
public class RedisController {

    @Autowired
    private MessageProducer messageProducer;

    @GetMapping("/lPush")
    public void lPush() {
        messageProducer.lPush();
    }
}

测试

Http://localhost:8080/lPush

可能出现的问题:

1.通过定时器监听List中是否有待处理消息,每执行一次都会发起一次连接,这会造成不必要的浪费。

2.生产速度大于消费速度,队列堆积,消息时效性差,占用内存。

运行即监控队列

修改消息消费者代码。

当队列没有元素时,会阻塞10秒,然后再次监听队列,
需要注意的是,阻塞时间必须小于连接超时时间

@Slf4j
@Component
@EnableScheduling
public class MessageConsumer {

    public static final String MESSAGE_KEY = "message:queue";

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    //@Scheduled(initialDelay = 5 * 1000, fixedRate = 2 * 1000)
    public void rPop() {
        String message = (String) redisTemplate.opsForList().rightPop(MESSAGE_KEY);
        log.info(message);
    }

    @PostConstruct
    public void brPop() {
        new Thread(() -> {
            while (true) {
                String message = (String) redisTemplate.opsForList().rightPop(MESSAGE_KEY, 10, TimeUnit.SECONDS);
                log.info(message);
            }
        }).start();
    }
}

阻塞时间不能为负,直接报错超时为负
阻塞时间为零,此时阻塞时间等于超时时间,最后报错连接超时
阻塞时间大于超时时间,报错连接超时

测试:

消息不可重复消费,因为消息从队列POP之后就被移除了,即不支持多个消费者消费同一批数据

消息丢失,消费期间发生异常,消息未能正常消费

四、发布/订阅模式

消息可以重复消费,多个消费者订阅同一频道即可

一个消费者根据匹配规则订阅多个频道

消费者只能消费订阅之后发布的消息,这意味着,消费者下线再上线这期间发布的消息将会丢失

数据不具有持久化。同样Redis宕机也会数据丢失

消息发布后,是推送到一个缓冲区(内存),消费者从缓冲区拉取消息,当消息堆积,缓冲区溢出,消费者就会被迫下线,同时释放对应的缓冲区

RedisConfig中添加监听器

    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        //订阅频道,通配符*表示任意多个占位符
        container.addMessageListener(new MySubscribe(), new PatternTopic("channel*"));

        return container;
    }

订阅者

package com.yzm.redis08.message;

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

public class MySubscribe implements MessageListener {

    @Override
    public void onMessage(Message message, byte[] bytes) {
        System.out.println("订阅频道:" + new String(message.getChannel()));
        System.out.println("接收数据:" + new String(message.getBody()));
    }
}

消息发布

    @GetMapping("/publish")
    public void publish() {
        redisTemplate.convertAndSend("channel_first", "hello world");
    }

另一种发布方式

    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        //订阅频道,通配符*表示任意多个占位符
        container.addMessageListener(new MySubscribe(), new PatternTopic("channel*"));
        // 通配符?:表示一个占位符
        MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(new MySubscribe2(), "getMessage");
        listenerAdapter.afterPropertiesSet();
        container.addMessageListener(listenerAdapter, new PatternTopic("channel?"));

        return container;
    }
public class MySubscribe2 {

    public void getMessage(Object message, String channel) {
        System.out.println("订阅频道2:" + channel);
        System.out.println("接收数据2:" + message);
    }
}
    @GetMapping("/publish2")
    public void publish2() {
        redisTemplate.convertAndSend("channel2", "hello world");
    }

消息是实体对象,进行转换

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 5250232737975907491L;
    private Integer id;
    private String username;
}
public class MySubscribe3 implements MessageListener {

    @Override
    public void onMessage(Message message, byte[] bytes) {
        Jackson2JsonRedisSerializer<User> jacksonSerializer = new Jackson2JsonRedisSerializer<>(User.class);
        jacksonSerializer.setObjectMapper(ObjectMapperConfig.objectMapper);
        User user = jacksonSerializer.deserialize(message.getBody());
        
        System.out.println("订阅频道3:" + new String(message.getChannel()));
        System.out.println("接收数据3:" + user);
    }
}
    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);

        //订阅频道,通配符*:表示任意多个占位符
        container.addMessageListener(new MySubscribe(), new PatternTopic("channel*"));
        // 通配符?:表示一个占位符
        MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(new MySubscribe2(), "getMessage");
        listenerAdapter.afterPropertiesSet();
        container.addMessageListener(listenerAdapter, new PatternTopic("channel?"));

        container.addMessageListener(new MySubscribe3(), new PatternTopic("user"));

        return container;
    }

    @GetMapping("/publish3")
    public void publish3() {
        User user = User.builder().id(1).username("yzm").build();
        redisTemplate.convertAndSend("user", user);
    }

五、ZSet实现延迟队列

生产消息,score = 时间搓+60s随机数

    public static final String MESSAGE_ZKEY = "message:ZSetqueue";
    public volatile AtomicInteger count =  new AtomicInteger();
    public void zAdd() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                int increment = count.getAndIncrement();
                log.info(Thread.currentThread().getName() + ":put message to zset = " + increment);
                double score = System.currentTimeMillis() + new Random().nextInt(60 * 1000);
                redisTemplate.opsForZSet().add(MESSAGE_ZKEY, Thread.currentThread().getName() + " hello zset:" + increment, score);
            }).start();
        }
    }

消费者:定时任务,每秒执行一次

    public static final String MESSAGE_ZKEY = "message:ZSetqueue";
    public SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
    @Scheduled(initialDelay = 5 * 1000, fixedRate = 1000)
    public void zrangebysocre() {
        log.info("延时队列消费。。。");
        // 拉取score小于当前时间戳的消息
        Set<Object> messages = redisTemplate.opsForZSet().rangeByScore(MESSAGE_ZKEY, 0, System.currentTimeMillis());
        if (messages != null) {
            for (Object message : messages) {
                Double score = redisTemplate.opsForZSet().score(MESSAGE_ZKEY, message);
                log.info("消费了:" + message + "消费时间为:" + simpleDateFormat.format(score));
                redisTemplate.opsForZSet().remove(MESSAGE_ZKEY, message);
            }
        }
    }
    @GetMapping("/zadd")
    public void zadd() {
        messageProducer.zAdd();
    }

 到此这篇关于SpringBoot整合redis之消息队列的文章就介绍到这了,更多相关springboot redis消息队列内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: springboot整合redis之消息队列

本文链接: https://www.lsjlt.com/news/153010.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • springboot整合redis之消息队列
    目录一、项目准备二、配置类三、redis中list数据类型定时器监听队列运行即监控队列四、发布/订阅模式五、ZSet实现延迟队列一、项目准备 依赖 <!-- R...
    99+
    2022-11-13
  • springboot整合消息队列RabbitMQ
    前言: RabbitMQ常用的三种Exchange Type:fanout、direct、topic。 fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。...
    99+
    2022-11-13
  • SpringBoot整合rockerMQ消息队列详解
    目录Springboot整合RockerMQ使用总结消费模式生产者组和消费者组生产者投递消息的三种方式如何保证消息不丢失顺序消息分布式事务Springboot整合RockerMQ 1...
    99+
    2022-11-13
  • SpringBoot整合RabbitMQ消息队列的完整步骤
    SpringBoot整合RabbitMQ 主要实现RabbitMQ以下三种消息队列: 简单消息队列(演示direct模式) 基于RabbitMQ特性的延时消息队列 ...
    99+
    2022-11-12
  • SpringBoot整合消息队列RabbitMQ的方法是什么
    本篇内容介绍了“SpringBoot整合消息队列RabbitMQ的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!简介在Spring...
    99+
    2023-07-05
  • Spring整合消息队列RabbitMQ流程
    目录搭建生产者工程创建工程添加依赖配置整合发送消息搭建消费者工程创建工程添加依赖配置整合消息监听器搭建生产者工程 创建工程 添加依赖 修改pom.xml文件内容为如下: <...
    99+
    2023-03-20
    Spring RabbitMQ Spring整合消息队列 Spring整合RabbitMQ
  • Springboot整合ActiveMQ实现消息队列的过程浅析
    目录pom中导入坐标书写yml配置业务层代码监听器代码业务层代码确保你启动了自己电脑的activemq。 pom中导入坐标 <dependency> ...
    99+
    2023-02-10
    Springboot整合ActiveMQ Springboot ActiveMQ消息队列
  • redis中的消息队列
    这期内容当中的小编将会给大家带来有关redis中的消息队列介绍,以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、认识消息队列1.1 消息队列概念“消息”是在两台计算机间传送的数据单位。...
    99+
    2022-11-30
    redis 消息队列 edi
  • kafka之消息队列
    大数据工具 kafka 学习 之前需要先了解队列的相关知识 了解万队列就知道kafka的用处 之后再详细了解kafka的具体知识和操作 ...
    99+
    2021-05-31
    kafka之消息队列
  • Redis中如何实现消息队列和延时消息队列
    这篇文章将为大家详细讲解有关Redis中如何实现消息队列和延时消息队列,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。list的几个命令lpush (left push)由...
    99+
    2022-10-19
  • 怎么在springboot中用redis实现消息队列
    本篇内容主要讲解“怎么在springboot中用redis实现消息队列”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么在springboot中用redis实现消息队列”吧!准备阶段安装redi...
    99+
    2023-06-19
  • redis消息队列是什么
    redis消息队列是什么?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!队列是一种特殊的线性表,特殊之处在于它只允许在表的前...
    99+
    2022-10-18
  • SpringBoot2如何整合Redis哨兵集群 实现消息队列场景
    这篇文章主要介绍了SpringBoot2如何整合Redis哨兵集群 实现消息队列场景,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、Redis集群简介1、RedisClus...
    99+
    2023-06-02
  • 如何监听redis消息队列
    监听redis消息队列的示例:springBoot实现redis发布式监听消息队列,定时器监听不同功能的消息,代码:import org.springframework.context.annotation.Bean;import org....
    99+
    2022-10-19
  • 怎么使用redis消息队列
    要使用Redis作为消息队列,你需要按照以下步骤进行操作:1. 安装并启动Redis服务。2. 在你的应用程序中引入Redis的客户...
    99+
    2023-08-24
    redis
  • 详解Redis Stream做消息队列
    目录ListPub/subStreamConsumer Grouplast_delivered_idpending_idscurdpending_ids如何避免消息丢失嵌入SpringBoot注册Redis s...
    99+
    2022-09-23
  • redis消息队列如何延时
    redis消息队列延时的示例:延时队列可通过zset来实现,消息的处理时间作为score,最后通过多线程轮询获取到期的score任务即可,代码:public class DelayQueue {static class TaskItem {...
    99+
    2022-10-17
  • redis怎么实现消息队列
    Redis可以通过以下几种方式实现消息队列:1. List数据结构:使用Redis的List数据结构实现简单的消息队列。生产者将消息...
    99+
    2023-09-14
    redis
  • thinkphp6 消息队列think-queue(完整版)
    1.安装队列依赖 如果是在Linux上,进入thinkphp项目的think文件所在目录,执行安装命令 composer require topthink/think-queue 修改queue的配置文件,文件位置config/queue....
    99+
    2023-10-21
    php 服务器 thinkphp queue
  • Redis在消息队列中的妙用
    Redis在消息队列中的妙用消息队列是一种常见的解耦架构,用于在应用程序之间传递异步消息。通过将消息发送到队列中,发送者可以在不等待接收者响应的情况下继续执行其他任务。而接收者可以在适当的时间从队列中获取消息并进行处理。Redis是一种常用...
    99+
    2023-11-07
    redis 消息队列 妙用
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作