SpringBoot集成RabbitMQ配置封装(第五节)

目录

 开始语

📝简述

🖋️封装RabbitMQConfig配置类

🖋️封装Callback配置类

🖋️封装请求类Entity

🖋️创建User对象 

🖋️创建消费者

🖋️创建生产者

😎运行查看结果

结束语


 开始语

一位普通的程序员,慢慢在努力变强!

RabbitMQ部署方式(第一节)

SpringBoot集成RabbitMQ(第二节)

SpringBoot集成RabbitMQ之ACK确认机制(第三节)

SpringBoot集成RabbitMQ之死信队列、限流队列、延迟队列(第四节)

📝简述

在前四篇文章中,都是对RabbitMQ的模式了解和基本使用,并且在第四接中讲述到了一些使用场景,但是在真实的开发中,不足的是配置和日志分析问题。上面四讲都是一些基本的使用,需要使用到什么功能就直接使用,没有配置的一个说明。 接下来,这章节会对mq的配置进行统一封装,简化mq的使用,一起学习,探讨吧!

🖋️封装RabbitMQConfig配置类

import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;

/**
 * MQ配置类
 *
 * @author tianyu.Ge
 * @date 2023/2/7 9:10
 */
@Configuration
public class RabbitConfig implements RabbitListenerConfigurer {


    /**
     * ==================设置请求body数据结构为:json=====================
     */
    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }


    /**
     * ==================响应接收json类型自动转换javabean=====================
     */
    @Override
    public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
    }

    @Bean
    MessageHandlerMethodFactory messageHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
        messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter());
        return messageHandlerMethodFactory;
    }

    @Bean
    public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
        return new MappingJackson2MessageConverter();
    }

}

🖋️封装Callback配置类

import cn.hutool.json.JSONUtil;
import com.net.entity.request.RabbitMQRequestEntity;
import com.net.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 消息发送确认
 * ConfirmCallback  只确认消息是否正确到达 Exchange 中
 * ReturnCallback   消息没有正确到达队列时触发回调,如果正确到达队列不执行
 * 1. 如果消息没有到exchange,则confirm回调,ack=false
 * 2. 如果消息到达exchange,则confirm回调,ack=true
 * 3. exchange到queue成功,则不回调return
 * 4. exchange到queue失败,则回调return
 *
 * @author 猿仁
 * @date 2023/2/11 20:50
 */
@Slf4j
@Component
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    @Autowired
    private RabbitConfig rabbitConfig;

    /**
     * 配置统一head...等
     *
     * @param
     * @return org.springframework.amqp.core.MessagePostProcessor
     * @author tianyu.Ge
     * @date 2022/9/13 10:25
     */
    public static final MessagePostProcessor messagePostProcessor =
            new MessagePostProcessor() {
                @Override
                public Message postProcessMessage(Message message) throws AmqpException {
                    message.getMessageProperties().setHeader("content_type", MessageProperties.CONTENT_TYPE_JSON);
                    return message;
                }
            };


    /**
     * ==================设置请求body数据结构为:json=====================
     */
    @Bean
    public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(rabbitConfig.producerJackson2MessageConverter());
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
        rabbitTemplate.setMandatory(true);
        return rabbitTemplate;
    }

    /**
     * ==================配置admin管理MQ相关信息=====================
     */
    @Bean
    public RabbitAdmin rabbitAdmin(final ConnectionFactory createConnectionFactory) {
        return new RabbitAdmin(createConnectionFactory);
    }

    /**
     * confirm机制只保证消息到达exchange,不保证消息可以路由到正确的queue,如果exchange错误,就会触发confirm机制
     *
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        // 消息发送时间
        Date date = new Date();
        String format = DateUtil.format(date, DateUtil.YYYY_MM_DD_HH_MM_SS);
        String id = correlationData.getId();
        String jsonData = new String(correlationData.getReturnedMessage().getBody());
        RabbitMQRequestEntity rabbitMQRequest = JSONUtil.toBean(JSONUtil.toJsonStr(jsonData), RabbitMQRequestEntity.class);
        if (ack) {
            log.info("[MQProducerAckConfig.confirm]消息发送成功通道id[{}]时间[{}]  BODY={}", id, format, rabbitMQRequest);
        } else {
            log.error("[MQProducerAckConfig.confirm]消息发送失败通道id[{}]时间[{}] cause={}  BODY={}", id, format, cause, rabbitMQRequest);
        }
    }

    /**
     * Return 消息机制用于处理一个不可路由的消息。在某些情况下,如果我们在发送消息的时候,当前的 exchange 不存在或者指定路由 key 路由不到,这个时候我们需要监听这种不可达的消息
     * 就需要这种return机制
     *
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        RabbitMQRequestEntity rabbitMQRequest = JSONUtil.toBean(JSONUtil.toJsonStr(new String(message.getBody())), RabbitMQRequestEntity.class);
        String format = DateUtil.format(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS);
        // 反序列化对象输出
        log.info("[MQProducerAckConfig.returnedMessage]消息送达MQ异常_业务id[{}]时间[{}]  \n 消息主体: {} \n 应答码: {} \n 描述: {} \n 消息使用的交换器: {} \n 消息使用的路由键: {}"
                , rabbitMQRequest.getBusinessId()
                , format
                , rabbitMQRequest.getData()
                , replyCode
                , replyText
                , exchange
                , routingKey);
    }
}

🖋️封装请求类Entity

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author tianyu.Ge
 * @description 消息内容
 * @date 2022/9/8
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class RabbitMQRequestEntity<T> implements Serializable {

    /**
     * 非必填,后台可自动生成  applicationName_随机日期id
     * 业务id,用于识别集体业务(也是消息应答重试的主要来源)
     */
    private String businessId;

    /**
     * 业务数据
     */
    private T data;

    /**
     * 发送者主题
     */
    public String sendTopic;

    /**
     * 业务描述
     */
    private String description;

    /**
     * 发送消息的_项目名称
     */
    private String sendApplicationName;

}

🖋️创建User对象 

import lombok.Data;

import java.io.Serializable;

@Data
public class User implements Serializable {

    // redis存储id
    private String id;
    // 消息体
    private String message;
}

🖋️创建消费者

import com.net.entity.User;
import com.net.entity.request.RabbitMQRequestEntity;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 第四种模型:路由模式
 * 死信队列之:重试次数达到最大次数,死信开始消费
 */
@Component
@Slf4j
public class RouteDeadLetterCustomer {
    public static final String QUEUE = "queue";
    public static final String ACK = "ack";
    public static final String EXCHANGE = "route_direct";

    /**
     * 死信队列使用:重试除数达到最大
     */
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(name = QUEUE),
            exchange = @Exchange(value = EXCHANGE),
            key = ACK)})
    public void queue(RabbitMQRequestEntity<User> entity, Channel channel, Message message, @Headers Map<String, Object> headers) throws Exception {
        try {
            log.info("交换机模式【direct】,key【queue】,开始消费:{},Channel:{}, Message:{}, Headers :{}", entity, channel, message, headers);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
        }
    }


}

🖋️创建生产者

import cn.hutool.json.JSONUtil;
import com.net.config.MQProducerAckConfig;
import com.net.entity.User;
import com.net.entity.request.RabbitMQRequestEntity;
import com.net.listener.consumer.RouteDeadLetterCustomer;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.UUID;

@SpringBootTest
class SpringbootRabbitmqPracticeApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Test
    void contextLoads() {
        
        User user = new User();
        user.setId("123");
        user.setMessage("name");
        RabbitMQRequestEntity build = RabbitMQRequestEntity.builder()
                .businessId(UUID.randomUUID().toString())
                .data(user)
                .description("测试一下配置功能")
                .sendApplicationName("rabbitmq")
                .sendTopic("rabbitmq_发送测试demo")
                .build();

        CorrelationData correlationData = new CorrelationData();
        correlationData.setReturnedMessage(new Message(JSONUtil.toJsonStr(build).getBytes(),  new MessageProperties()));
        rabbitTemplate.convertAndSend(RouteDeadLetterCustomer.EXCHANGE, RouteDeadLetterCustomer.ACK, build, MQProducerAckConfig.messagePostProcessor, correlationData);
    }

}

😎运行查看结果

温馨提示------------------------------------------

 

结束语

温馨提示:如有问题,可在下方留言,作者看到了会第一时间回复!

本章节完成了,各位正在努力的程序员们,如果你们觉得本文章对您有用的话,你学到了一些东西,希望猿友们点个赞+关注,支持一下猿仁!
持续更新中…

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猿仁

多一分支持,多一分动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值