SpringBoot 整合 RabbitMQ

本文探讨了RabbitMQ在异步处理、应用解耦、流量控制等方面的应用,介绍了消息中间件的工作模式,如点对点和发布订阅,以及如何在SpringBoot中整合RabbitMQ,包括配置、确认机制和延迟队列的使用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用场景:

  • 异步处理:如果用户注册后需要给用户发送邮件和短信,那么就可以使用消息队列中间件进行异步发送,因为发送邮件和发送短信这两个操作没有任何的关联顺序。
  • 应用解耦:例如订单系统调用库存系统的时候,可以加个中间件做到应用解耦。
  • 流量控制:流量控制也叫流量削峰,是说当高并发的时候或者是秒杀的场景下,可以将流量都放到消息队列中,然后处理秒杀的业务再以此进行处理,避免高并发下服务宕机。

消息中间件的模式:

  • 点对点发布模式:消息发送者发布消息到消息代理,消息代理将消息放入队列,消息接收者从队列中获取消息,之后队列移除消息,消息可以有多个接收者进行处理
  • 发布订阅模式:消息发送者发送到主题,多个消息接收者对这个主题进行监听,那么就会在消息到达的时候同时受到消息

概念

  • Publisher生产者,会与代理服务器Broker建立一条长连接,并且开辟出很多信道Chanel,通过这些Chanel将消息发送给Broker
  • Message 消息(头,路由键 + 体)
  • Broker:代理服务器(有多个交换机多个队列)
    • VHost:用于做隔离,例如隔离生不同语言之间的消息管理或者是生产环境和开发环境之间的隔离
      • Exchange 交换机,负责接收消息,交换机和队列之间有绑定关系Binding,交换机根据消息头中的路由键转发到相应的队列里
      • Exchange 交换机,负责接收消息,交换机和队列之间有绑定关系Binding,交换机根据消息头中的路由键转发到相应的队列里
      • Exchange 交换机,负责接收消息,交换机和队列之间有绑定关系Binding,交换机根据消息头中的路由键转发到相应的队列里
      • Queue队列,存储消息
      • Queue队列,存储消息
      • Queue队列,存储消息
  • Consumer消费者,一个消费者也可以与代理服务器Broker建立一条长连接,并开辟很多Chanel,这些Chanel可以监听队列中的消息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安装

[root@localhost ~]# docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management
[root@localhost ~]# docker update rabbitmq --restart=always
rabbitmq

交换机类型:

  • Direct 直连,交换机会根据路由键精确匹配到一个队列上
  • fanout广播,交换机会给已经绑定的所有队列群发消息
  • topic 根据模糊匹配规则#和*进行路由匹配发送给队列消息,#代表多个字符, * 代表必须要有一个字符

SpringBoot整合RabbitMQ

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency> 

在主启动类中开启@EnableRabbit

配置yml文件

spring:
  rabbitmq:
    port: 5672
    host: 192.168.100.10
    virtual-host: /
    publisher-confirms: true  #开启手动确认机制
    publisher-returns: true
    template:
      mandatory: true
    listener:
      simple:
        acknowledge-mode: manual

配置mq配置类,不选择的话是使用的jdk虚拟化而是使用JackSon的虚拟化,保证在传输对象到队列的时候显示json数据

@Configuration
public class MyRabbitConfig {
    @Bean 
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

测试,创建exchange、binding、queue、message

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallOrderApplicationTests {
    @Autowired
    AmqpAdmin amqpAdmin;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Test
    public void sendMsg() {
        String msg = "hello world";
        rabbitTemplate.convertAndSend("hello.java.exchange","hello.java.exchange",msg);

        log.info("发送的消息是{}",msg);
    }
    @Test
    public void createExchange() {
        amqpAdmin.declareExchange(new DirectExchange("hello.java.exchange",true,false));
        log.info("exchange创建成功hello.java.exchange");
    }

    @Test
    public void createQueue() {
        Queue queue = new Queue("hello.java.queue",true,false,false);
        amqpAdmin.declareQueue(queue);
        log.info("queue创建成功hello.java.queue");
    }

    @Test
    public void createBinding() {
        Binding binding = new Binding("hello.java.queue",
                Binding.DestinationType.QUEUE,
                "hello.java.exchange",
                "hello.java.exchange",
                null
                );
        amqpAdmin.declareBinding(binding);
        log.info("binding成功");
    }

两个重要的注解:

  • @RabbitListener(queues = {“hello.java.queue”})这个注解可以标识在类和方法中
  • @RabbitHandler这个注解只能放在方法上,并且搭配@RabbitListener(queues = {“hello.java.queue”})作为类注解来使用,使用场景是当消息中有不同类型的消息的时候,我们需要让不同的方法接收不同的消息进行处理,这时候我们可是用@RabbitHandler作为方法注解来重载不同的方法,来解决这个问题

消息确认机制

​ 保证消息不丢失,可靠抵达,可以使用事务机制,但是性能会下降250倍,为此可以引入确认机制

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

消息确认机制配置

# RabbitMQ配置
spring.rabbitmq.host=192.168.77.130
spring.rabbitmq.port=5672
# 虚拟主机配置
spring.rabbitmq.virtual-host=/
# 开启发送端消息抵达Broker确认
spring.rabbitmq.publisher-confirms=true
# 开启发送端消息抵达Queue确认
spring.rabbitmq.publisher-returns=true
# 只要消息抵达Queue,就会异步发送优先回调returnfirm
spring.rabbitmq.template.mandatory=true
# 手动ack消息,不使用默认的消费端确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@Configuration
public class MyRabbitConfig {

    private RabbitTemplate rabbitTemplate;

    @Primary
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter());
        initRabbitTemplate();
        return rabbitTemplate;
    }

    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     * 1、服务收到消息就会回调
     *      1、spring.rabbitmq.publisher-confirms: true
     *      2、设置确认回调
     * 2、消息正确抵达队列就会进行回调
     *      1、spring.rabbitmq.publisher-returns: true
     *         spring.rabbitmq.template.mandatory: true
     *      2、设置确认回调ReturnCallback
     *
     * 3、消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)
     *
     */
    // @PostConstruct  //MyRabbitConfig对象创建完成以后,执行这个方法
    public void initRabbitTemplate() {

        /**
         * 1、只要消息抵达Broker就ack=true
         * correlationData:当前消息的唯一关联数据(这个是消息的唯一id)
         * ack:消息是否成功收到
         * cause:失败的原因
         */
        //设置确认回调
        rabbitTemplate.setConfirmCallback((correlationData,ack,cause) -> {
            System.out.println("confirm...correlationData["+correlationData+"]==>ack:["+ack+"]==>cause:["+cause+"]");
        });


        /**
         * 只要消息没有投递给指定的队列,就触发这个失败回调
         * message:投递失败的消息详细信息
         * replyCode:回复的状态码
         * replyText:回复的文本内容
         * exchange:当时这个消息发给哪个交换机
         * routingKey:当时这个消息用哪个路邮键
         */
        rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey) -> {
            System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]" +
                    "==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]");
        });
    }
}

创建一个订单的延迟队列和普通队列并设置延迟队列的路由键,当消息过期以后路由到取消队列

@Configuration
public class MyRabbitConfig {

    /**
     * 使用JSON序列化机制,进行消息转换
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    // @RabbitListener(queues = "stock.release.stock.queue")
    // public void handle(Message message) {
    //
    // }

    /**
     * 库存服务默认的交换机
     * @return
     */
    @Bean
    public Exchange stockEventExchange() {
        //String name, boolean durable, boolean autoDelete, Map<String, Object> arguments

        TopicExchange topicExchange = new TopicExchange("stock-event-exchange", true, false);
        return topicExchange;
    }

    /**
     * 普通队列
     * @return
     */
    @Bean
    public Queue stockReleaseStockQueue() {
        //String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        Queue queue = new Queue("stock.release.stock.queue", true, false, false);
        return queue;
    }


    /**
     * 延迟队列
     * @return
     */
    @Bean
    public Queue stockDelay() {

        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "stock-event-exchange");
        arguments.put("x-dead-letter-routing-key", "stock.release");
        // 消息过期时间 2分钟
        arguments.put("x-message-ttl", 120000);

        Queue queue = new Queue("stock.delay.queue", true, false, false,arguments);
        return queue;
    }


    /**
     * 交换机与普通队列绑定
     * @return
     */
    @Bean
    public Binding stockLocked() {
        //String destination, DestinationType destinationType, String exchange, String routingKey,
        //           Map<String, Object> arguments
        Binding binding = new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.release.#",
                null);

        return binding;
    }


    /**
     * 交换机与延迟队列绑定
     * @return
     */
    @Bean
    public Binding stockLockedBinding() {
        return new Binding("stock.delay.queue",
                Binding.DestinationType.QUEUE,
                "stock-event-exchange",
                "stock.locked",
                null);
    }

}

向指定交换机和指定队列发送消息

rabbitTemplate.convertAndSend("stock-event-exchange","stock.release",orderEntityTo);

创建监听器监听发来的消息

@Service
@RabbitListener(queues = "order.release.order.queue")
public class OrderCloseListener {
    @Autowired
    OrderService orderService;
    @RabbitHandler
    public void orderClose(OrderEntity orderEntity, Channel channel, Message message) throws IOException {
        try {
            orderService.orderClose(orderEntity);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
}
Spring Boot框架可以很容易地与RabbitMQ进行集成。为了实现这个目标,你需要在项目的依赖项中添加两个关键的依赖项。首先,你需要添加spring-boot-starter-amqp依赖项,它提供了与RabbitMQ进行通信的必要类和方法。其次,你还需要添加spring-boot-starter-web依赖项,以便在项目中使用Web功能。 在你的项目中创建两个Spring Boot应用程序,一个是RabbitMQ的生产者,另一个是消费者。通过这两个应用程序,你可以实现消息的发送和接收。生产者应用程序负责将消息发送到RabbitMQ的消息队列,而消费者应用程序则负责从队列中接收并处理消息。这样,你就可以实现基于RabbitMQ的消息传递系统。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合RabbitMQ](https://blog.youkuaiyun.com/K_kzj_K/article/details/106642250)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Springboot 整合RabbitMq ,用心看完这一篇就够了](https://blog.youkuaiyun.com/qq_35387940/article/details/100514134)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [undefined](undefined)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值