zoukankan      html  css  js  c++  java
  • SpringBoot集成RabbitMQ

    pom:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    yml:
    spring:
      rabbitmq:
        host: ip
        port: 5672
        virtual-host: /
        username: 账号
        password: 密码
     
    1.直连型交换机
    根据消息携带的路由键将消息投递给对应队列。
    流程:有一个队列绑定到一个直连交换机上,同时设置一个路由键。然后当一个消息携带着路由值X,这个消息通过生产者发送给交换机,交换机就会根据这个路由值X去寻找绑定值也是X的队列。
    生产者:
    //直连型交换机
    @Configuration
    public class DirectRabbitConfig {
    
        //队列 起名:TestDirectQueue
        @Bean
        public Queue TestDirectQueue() {
            // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效。
            // exclusive:默认是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。
            // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。//一般设置一下队列的持久化就好,其余两个就是默认false
            return new Queue("TestDirectQueue",true, false, false);
        }
    
        //Direct交换机 起名:TestDirectExchange
        @Bean
        DirectExchange TestDirectExchange() {
            return new DirectExchange("TestDirectExchange",true,false);
        }
    
        //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting
        @Bean
        Binding bindingDirect() {
            return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting4");
        }
    
    }
    package com.demo.business.controller;
    
    import com.demo.common.msg.BaseResponse;
    import com.demo.common.util.ResponseMsgUtil;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    @RestController
    @RequestMapping("/mq")
    public class RabbitMQController {
    
        @Autowired
        RabbitTemplate rabbitTemplate;
    
        @GetMapping("/send")
        public BaseResponse send() {
            String messageId = String.valueOf(UUID.randomUUID());
            String messageData = "test message, hello!";
            String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            Map<String,Object> map=new HashMap<>();
            map.put("messageId",messageId);
            map.put("messageData",messageData);
            map.put("createTime",createTime);
            //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
            rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);
            return ResponseMsgUtil.success();
        }
        
    }

    消费者:

    package com.demo.business.controller;
    
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    
    @Component
    @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
    public class DirectReceiver {
    
        @RabbitHandler
        public void process(Map testMessage) {
            System.out.println("DirectReceiver消费者收到消息  : " + testMessage.toString());
        }
    
    }
    2.主题交换机
    主题交换机与直连交换机流程相似,区别在于绑定键有规则(*便是一个单词,#表示零个或多个)。
    生产者:
    package com.demo.config;
    
    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.core.TopicExchange;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class TopicRabbitConfig {
        //绑定键
        public final static String man = "topic.man";
        public final static String woman = "topic.woman";
    
        @Bean
        public Queue firstQueue() {
            return new Queue(TopicRabbitConfig.man);
        }
    
        @Bean
        public Queue secondQueue() {
            return new Queue(TopicRabbitConfig.woman);
        }
    
        @Bean
        TopicExchange exchange() {
            return new TopicExchange("topicExchange");
        }
    
        //将firstQueue和topicExchange绑定,而且绑定的键值为topic.man
        //这样只要是消息携带的路由键是topic.man,才会分发到该队列
        @Bean
        Binding bindingExchangeMessage() {
            return BindingBuilder.bind(firstQueue()).to(exchange()).with(man);
        }
    
        //将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#
        // 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列
        @Bean
        Binding bindingExchangeMessage2() {
            return BindingBuilder.bind(secondQueue()).to(exchange()).with("topic.#");
        }
    
    }
    @GetMapping("/send2")
        public BaseResponse send2() {
            String messageId = String.valueOf(UUID.randomUUID());
            String messageData = "test message, hello!";
            String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            Map<String,Object> map=new HashMap<>();
            map.put("messageId",messageId);
            map.put("messageData",messageData);
            map.put("createTime",createTime);
            //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
            rabbitTemplate.convertAndSend("topicExchange", "topic.woman22222", map);
            return ResponseMsgUtil.success();
        }

    消费者:

    import java.util.Map;
    
    @Component
    @RabbitListener(queues = "topic.woman")//队列名称
    public class TopicManReceiver {
    
        @RabbitHandler
        public void process(Map testMessage) {
            System.out.println("TopicManReceiver消费者收到消息  : " + testMessage.toString());
        }
    
    }
    3.扇形交换机
    没有路由键的概念,接收到消息后,会直接转发到绑定到它上面的所有队列。
    生产者:
    package com.demo.config;
    
    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.FanoutExchange;
    import org.springframework.amqp.core.Queue;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FanoutRabbitConfig {
    
        @Bean
        public Queue queueA() {
            return new Queue("fanout.A");
        }
    
        @Bean
        public Queue queueB() {
            return new Queue("fanout.B");
        }
    
        @Bean
        public Queue queueC() {
            return new Queue("fanoutC");
        }
    
        @Bean
        FanoutExchange fanoutExchange() {
            return new FanoutExchange("fanoutExchange");
        }
    
        @Bean
        Binding bindingExchangeA() {
            return BindingBuilder.bind(queueA()).to(fanoutExchange());
        }
    
        @Bean
        Binding bindingExchangeB() {
            return BindingBuilder.bind(queueB()).to(fanoutExchange());
        }
    
        @Bean
        Binding bindingExchangeC() {
            return BindingBuilder.bind(queueC()).to(fanoutExchange());
        }
    
    }
    @GetMapping("/send3")
    public BaseResponse send3() {
      String messageId = String.valueOf(UUID.randomUUID());
      String messageData = "test message, hello!";
      String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
      Map<String,Object> map=new HashMap<>();
      map.put("messageId",messageId);
      map.put("messageData",messageData);
      map.put("createTime",createTime);
      //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
      rabbitTemplate.convertAndSend("fanoutExchange", null, map);
      return ResponseMsgUtil.success();
    }

    消费者:

    ackage com.demo.business.controller;
    
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    
    @Component
    @RabbitListener(queues = "fanout.A")
    public class FanoutReceiverA {
    
        @RabbitHandler
        public void process(Map testMessage) {
            System.out.println("FanoutReceiverA消费者收到消息  : " +testMessage.toString());
        }
    
    }
    package com.demo.business.controller;
    
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    
    @Component
    @RabbitListener(queues = "fanoutC")
    public class FanoutReceiverC {
    
        @RabbitHandler
        public void process(Map testMessage) {
            System.out.println("FanoutReceiverC消费者收到消息  : " +testMessage.toString());
        }
    
    }

     另外,还有Header Exchange 头交换机、Default Exchange 默认交换机、Dead Letter Exchange 死信交换机。

    4.消息回调
    生产这发送消息结束后回调。
    yml增加配置:
    spring:
        #确认消息已发送到交换机(Exchange)
        publisher-confirms: true
        #确认消息已发送到队列(Queue)
        publisher-returns: true
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.connection.ConnectionFactory;
    import org.springframework.amqp.rabbit.connection.CorrelationData;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    @Configuration
    public class RabbitConfig {
     
        @Bean
        public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
            RabbitTemplate rabbitTemplate = new RabbitTemplate();
            rabbitTemplate.setConnectionFactory(connectionFactory);
            //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
            rabbitTemplate.setMandatory(true);
     
            rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
                @Override
                public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                    System.out.println("ConfirmCallback:     "+"数据:"+correlationData);
                    System.out.println("ConfirmCallback:     "+"结果:"+ack);
                    System.out.println("ConfirmCallback:     "+"原因:"+cause);
                }
            });
     
            rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
                @Override
                public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                    System.out.println("ReturnCallback:     "+"消息:"+message);
                    System.out.println("ReturnCallback:     "+"状态码:"+replyCode);
                    System.out.println("ReturnCallback:     "+"信息:"+replyText);
                    System.out.println("ReturnCallback:     "+"交换机:"+exchange);
                    System.out.println("ReturnCallback:     "+"键值:"+routingKey);
                }
            });
     
            return rabbitTemplate;
        }
     
    }

    调用规则:

    找不到交换机,调用:ConfirmCallback;
    找到交换机,没找到队列,调用:ConfirmCallback和RetrunCallback;
    交换机和队列都没找到,调用:ConfirmCallback;
    消息推送成功,调用:ConfirmCallback;
    5.消息确认
    消费者收到消息后进行操作。
    手动确认:消费者收到消息后手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功。
    basicAck用于肯定确认
    basicNack用于否定确认
    basicReject用于否定确认,与basic.nack相比,一次只能拒绝单挑消息。
    import com.elegant.rabbitmqconsumer.receiver.MyAckReceiver;
    import org.springframework.amqp.core.AcknowledgeMode;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
    import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    @Configuration
    public class MessageListenerConfig {
     
        @Autowired
        private CachingConnectionFactory connectionFactory;
        @Autowired
        private MyListenerConfig myListenerConfig;//消息接收处理类
     
        @Bean
        public SimpleMessageListenerContainer simpleMessageListenerContainer() {
            SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
            container.setConcurrentConsumers(1);
            container.setMaxConcurrentConsumers(1);
            container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//RabbitMQ默认是自动确认,这里改为手动确认消息
            container.setQueueNames("TestDirectQueue");
            container.setMessageListener(myListenerConfig);
            return container;
        }
    }
    import com.rabbitmq.client.Channel;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
    import org.springframework.stereotype.Component;
    import java.util.HashMap;
    import java.util.Map;
     
    @Component
    public class MyListenerConfig implements ChannelAwareMessageListener {
     
        @Override
        public void onMessage(Message message, Channel channel) throws Exception {
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            try {
                System.out.println(消息:"+message.toString());
                channel.basicAck(deliveryTag, true);
                //channel.basicReject(deliveryTag, true);//为true会重新放回队列
            } catch (Exception e) {
                channel.basicReject(deliveryTag, false);
                e.printStackTrace();
            }
        }
    
    }

    代码:https://files.cnblogs.com/files/DreamFather/mq.zip
  • 相关阅读:
    【VS开发】【图像处理】RGB各种格式
    【VS开发】【图像处理】RGB Bayer Color分析
    【VS开发】【图像处理】RGB Bayer Color分析
    【VS开发】ActiveX开发注意事项
    【VS开发】ActiveX开发注意事项
    【VS开发】 自己编写一个简单的ActiveX控件——详尽教程
    【VS开发】 自己编写一个简单的ActiveX控件——详尽教程
    【VS开发】在VS2010中开发ActiveX控件设置测试容器的方式
    vim 编辑 windows下的文本时出现乱码, 修改配置后 已解决
    Centos 中扩展 软件源 的安装 之 epel ( 为yum 扩展软件源 EPEL源 )
  • 原文地址:https://www.cnblogs.com/DreamFather/p/13571406.html
Copyright © 2011-2022 走看看