springboot项目中整合RabbitMQ高级消息队列(技术篇)

注:此篇只是简单整合使用,不涉及RabbitMQ高级消息队列概念

1.在pom中导入rabbitMQ整合启动场景依赖


        
            org.springframework.boot
            spring-boot-starter-amqp
        

2.此时容器中自动配置了RabbitAutoConfiguration类,其给容器放四个重要的的对象

@Bean
		public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
				ObjectProvider connectionNameStrategy) 


@Bean
		@ConditionalOnSingleCandidate(ConnectionFactory.class)
		@ConditionalOnMissingBean(RabbitOperations.class)
		public RabbitTemplate rabbitTemplate(RabbitProperties properties,
				ObjectProvider messageConverter,
				ObjectProvider retryTemplateCustomizers,
				ConnectionFactory connectionFactory)

@Bean
		@ConditionalOnSingleCandidate(ConnectionFactory.class)
		@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
		@ConditionalOnMissingBean
		public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory)

@Bean
		@ConditionalOnSingleCandidate(RabbitTemplate.class)
		public RabbitMessagingTemplate rabbitMessagingTemplate(RabbitTemplate rabbitTemplate) 

3.主启动类上添加开启Rabbit注解

@EnableRabbit

4.编写配置文件

#配置rabbitMq的配置
#指定虚拟机的地址
spring.rabbitmq.host=145.85.127.158
#指定链接端口
spring.rabbitmq.port=5672
#哪个虚拟地址
spring.rabbitmq.virtual-host=/

5.使用AmqpAdmin进行创建Exchange,Queue,Binding等

@Autowired
    private AmqpAdmin amqpAdmin;

    /**
     * 如何创建Exchange,Queue,Binding
     * 1.使用AmqpAdmin进行创建
     */

    //创建交换机
    @Test
    public void createExchange() {
        //创建交换机-交换机名字,是否持久化,是否自动删除
        DirectExchange directExchange = new DirectExchange("java-exchange", true, false);
        amqpAdmin.declareExchange(directExchange);
        log.info("Exchange创建成功:[{}]", "java-exchange");
    }

    //创建队列
    @Test
    public void createQueue() {
        //创建一个队列-队列名字,是否持久,是否排他,是否自动删除
        Queue queue = new Queue("java-queue", true, false, false);
        amqpAdmin.declareQueue(queue);
        log.info("Queue创建成功:[{}]", "java-queue");
    }

    //创建一个绑定关系
    @Test
    public void createBinding() {
//        String destination,目的地
//        DestinationType destinationType,目的地类型
//        String exchange,交换机
//        String routingKey,路由键(重要)(通过路由键判断交换机把消息给到哪个队列 )
//        Map arguments自定义参数
        Binding binding = new Binding("java-queue"
                , Binding.DestinationType.QUEUE
                , "java-exchange"
                , "hello.java", null);
        amqpAdmin.declareBinding(binding);
        log.info("binding创建成功:[{}]", "java-binding");
    }

6.如何通过RabbitTemplate收发消息

@Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 如何创建Exchange,Queue,Binding
     * 1.使用AmqpAdmin进行创建
     * 2.如何收发消息
     */
    //发送消息
//    @Test
//    public void sendMessage(){
//        String msg = "hello world";
//        //转换和发送消息,交换机名字,路由键,消息(这里还可以传递对象)
//        rabbitTemplate.convertAndSend("java-exchange","hello.java",msg);
//        log.info("消息发送成功:[{}]",msg);
//    }
    //发送消息
    @Test
    public void sendMessage(){
        String msg = "hello world";
        //这个要传递的类必须实现序列化
        OrderReturnReasonEntity orderReturnReasonEntity = new OrderReturnReasonEntity();
        orderReturnReasonEntity.setId(1L);
        orderReturnReasonEntity.setName("哈哈");
        orderReturnReasonEntity.setCreateTime(new Date());

        //发送对象消息可以是一个json
        //转换和发送消息,交换机名字,路由键,消息(这里还可以传递对象)
        rabbitTemplate.convertAndSend("java-exchange","hello.java",orderReturnReasonEntity);
        log.info("消息发送成功:[{}]",orderReturnReasonEntity);
    }

7.监听消息:使用@RabbitListener和@RabbitHandler



@RabbitListener(queues = {"java-queue"})//标识监听这个队列
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl implements OrderItemService {


    /**
     * queues:声明需要监听的所有队列
     * Message:原生消息对象,头+体
     * OrderReturnReasonEntity:发送消息时候的封装对象
     * Channel:客户端连接通道对象
     * queue:可以很多人都来监听,只要有收到消息,队列就进行删除消息,而且只能有一个收到消息
     * @RabbitListener(queues = {"java-queue"})类+方法
     * @RabbitHandler 方法
     */
    @RabbitHandler //真正的消息队列处理在这里执行,根据这个参数OrderReturnReasonEntity的类型进行接收
    public void recieveMessage1(Message message, OrderReturnReasonEntity content,
                               Channel channel) {
        //获取消息体
        System.out.println("接收到的消息:" + message + "===>类型:" + content);
        byte[] body = message.getBody();
        //获取消息头
        MessageProperties messageProperties = message.getMessageProperties();
        //进行线程休眠
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println("消息处理完成:"+content.getName());
//        System.out.println("接收到的消息:"+message+"===>类型:"+message.getClass());
    }

    @RabbitHandler //真正的消息队列处理在这里执行,根据这个参数OrderReturnReasonEntity的类型进行接收
    public void recieveMessage2(Message message, OrderEntity content,
                               Channel channel) {
        //获取消息体
        System.out.println("接收到的消息:" + message + "===>类型:" + content);
        byte[] body = message.getBody();
        //获取消息头
        MessageProperties messageProperties = message.getMessageProperties();
        //进行线程休眠
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println("消息处理完成:"+content.getOrderSn());
//        System.out.println("接收到的消息:"+message+"===>类型:"+message.getClass());
    }

}

8.保证可靠抵达配置文件:

#开启确认消息已发送到交换机,选择确认类型为交互,ConfirmCallback调用这个函数
spring.rabbitmq.publisher-confirm-type=correlated
#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达就以异步方式优先回调这个returncallback函数
spring.rabbitmq.template.mandatory=true

#手动ack消息回复
spring.rabbitmq.listener.simple.acknowledge-mode=manual

9.保证可靠抵达服务端配置:

@Configuration
public class MyRabbitConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;//为实现可靠抵达,服务端需要进行定制化setConfirmCallback,setReturnCallback

    //如果容器中有消息转化对象,就用容器中的
    @Bean
    public MessageConverter getMessageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    //定制化消息队列模板
    @PostConstruct//MyRabbitConfig对象创建完成后,执行这个方法
    public void initRabbitTemplate(){
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * Confirmation callback.
             * @param correlationData correlation data for the callback.当前消息唯一关联数据
             * @param ack true for ack, false for nack  消息是否成功
             * @param cause An optional cause, for nack, when available, otherwise null.失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("comfirm....correlationData["+correlationData+"]==>ack["+ack+"]==>cause["+cause+"]");
            }
        });
        //设置消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递给指定的队列,就触发这个失败的回调
             * Returned message callback.
             * @param message the returned message. 投递失败的消息详情信息
             * @param replyCode the reply code.回复的状态码
             * @param replyText the reply text.回复的文本内容
             * @param exchange the exchange.当前消息发送给哪个交换机
             * @param routingKey the routing key.当时这个消息用哪个路由健
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return....message["+message+"]==>replyCode["+replyCode+"]==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]");
            }
        });
    }
}

10.保证可靠抵达ack(修改监听处理方法):

@RabbitHandler //真正的消息队列处理在这里执行,根据这个参数OrderReturnReasonEntity的类型进行接收
    public void recieveMessage1(Message message, OrderReturnReasonEntity content,
                               Channel channel) throws IOException {
        //获取消息体
        System.out.println("接收到的消息:" + message + "===>类型:" + content);
        byte[] body = message.getBody();
        //获取消息头
        MessageProperties messageProperties = message.getMessageProperties();
        //进行线程休眠
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println("消息处理完成:"+content.getName());
        /**
         * 为实现可靠抵达,消费端确认(保证每个消息被正确消费,此时才可以broker删除消息)
         *  1.默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除掉这个消息
         *         问题:我们收到很多消息,自动回复ack,只要有一个消息成功,发生的其他消息就会丢失
         *           我们要修改成手动确认:rabbit会自动把数据从Unacked中放入到Ready中下次服务器启动就会执行
         */
        //这个在channel通道中是自增的
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
//        System.out.println("接收到的消息:"+message+"===>类型:"+message.getClass());

        try {
            /**
             * 派发的标签
             * * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
             * * @param multiple true to acknowledge all messages up to and
             * 签收消息,非批量处理
             */
            //签收模式 进行判断签收
            if (deliveryTag%2==0){

                channel.basicAck(deliveryTag,false);
                System.out.println("签收了消息:"+deliveryTag);
            }else {
                System.out.println("没有签收消息:"+deliveryTag);
            }
        } catch (IOException e) {
            //网络中断
            e.printStackTrace();
            //退货模式
            /**
             * requeue true if the rejected message(s) should be requeued rather
             * 是否批量拒收,是否重新入队
             */
            channel.basicNack(deliveryTag,false,false);
            /**
             * requeue true if the rejected message should be requeued rather than discarded/dead-lettered
             */
            //channel.basicReject();
            System.out.println("拒绝签收消息:"+deliveryTag);
        }
    }

你可能感兴趣的:(rabbitmq,springboot,rabbitmq,spring,boot,java)