在SpringBoot中的使用RabbitMQ

本文介绍了如何在Spring Boot应用中集成RabbitMQ,配置消息队列、死信队列、交换机以及监听器。通过pom.xml引入依赖,application.properties设置队列和交换机参数,然后在配置类中定义队列、交换机、绑定,并设置回调处理。消息发送测试展示了如何发送消息,接收端通过监听器消费消息并处理异常。
摘要由CSDN通过智能技术生成

1、pom文件引入

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

2、在application.properties定义队列名和交换机名

spring.rabbitmq.host=192.168.19.23
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
# 开启confirms回调 P -> Exchange 发送确认
spring.rabbitmq.publisher-confirms=true
# 开启returnedMessage回调 Exchange -> Queue 路由失败回调
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true

# 签收模式为手动签收-那么需要在代码中手动ACK
spring.rabbitmq.listener.simple.acknowledge-mode=manual
# 每个队列最大的消费者数量
spring.rabbitmq.listener.simple.max-concurrency=1
# 每个队列启动的消费者数量
spring.rabbitmq.listener.simple.concurrency=1
# 每次从RabbitMQ获取的消息数量
spring.rabbitmq.listener.simple.prefetch=1

#定义消息队列
app.rabbitmq.queue.wx.oa=wx-oa-queue
#定义死信消息队列
app.rabbitmq.queue.wx.oa.dead.letter=wx-oa-dead-letter-queue
#交换机
app.rabbitmq.exchange.wx.oa=wx-oa-exchange
#死信交换机
app.rabbitmq.exchange.common.dead.letter=common-dead-letter-exchange
 

3、配置队列和交换机绑定

@Slf4j
@Configuration
public class RabbitConfig {
    @Autowired
    private CachingConnectionFactory connectionFactory;
    //队列名
    @Value("${app.rabbitmq.queue.wx.oa}")
    private String wxOAQueueName;
    //通用死信交换机名
    @Value("${app.rabbitmq.exchange.common.dead.letter}")
    private String commonDeadLetterExchange;
    //交换机名
    @Value("${app.rabbitmq.exchange.wx.oa}")
    private String wxOAExchangeName;

    @Value("${app.rabbitmq.queue.wx.oa.dead.letter}")
    private String wxOADeadLetterQueue;
    /**
     * 微信公众号消息队列
     *
     *
     * @return
     */
    @Bean
    public Queue wxOAQueue(
                           ) {
        return QueueBuilder
                .durable(wxOAQueueName)
                //声明该队列的死信消息发送到的 交换机 (队列添加了这个参数之后会自动与该交换机绑定,并设置路由键,不需要开发者手动设置)
                .withArgument("x-dead-letter-exchange", commonDeadLetterExchange)
                //声明该队列死信消息在交换机的 路由键
                .withArgument("x-dead-letter-routing-key", "wx-oa-dead-letter-routing-key")
                .build();
    }

    /**
     * 交换机
     *
     *
     * @return
     */
    @Bean
    public Exchange wxOAExchange() {
        return ExchangeBuilder
                .topicExchange(wxOAExchangeName)
                .durable(true)
                .build();
    }

    /**
     * 队列与交换机绑定
     *
     *
     *
     * @return
     */
    @Bean
    public Binding wxOABinding() {
        return BindingBuilder
                .bind(wxOAQueue())
                .to(wxOAExchange())
                .with("wx.oa.routing.key")
                .noargs();
    }

    /**
     * 死信交换机
     *
     *
     * @return
     */
    @Bean
    public Exchange commonDeadLetterExchange() {
        return ExchangeBuilder
                .topicExchange(commonDeadLetterExchange)
                .durable(true)
                .build();
    }


    /**
     * 队列的死信消息 路由的队列
     * 队列wx-oa-queue的死信投递到死信交换机`common-dead-letter-exchange`后再投递到该队列
     * 用这个队列来接收wx-oa-queue的死信消息
     *
     * @return
     */
    @Bean
    public Queue wxOADeadLetterQueue() {
        return QueueBuilder
                .durable(wxOADeadLetterQueue)
                .build();
    }

    /**
     * 死信队列绑定死信交换机
     *
     *
     *
     * @return
     */
    @Bean
    public Binding wxOADeadLetterBinding() {
        return BindingBuilder
                .bind(wxOADeadLetterQueue())
                .to(commonDeadLetterExchange())
                .with("wx-oa-dead-letter-routing-key")
                .noargs();
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(converter());
        // 消息是否成功发送到Exchange
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                String msgId = correlationData.getId();
                log.info("消息成功发送到Exchange",msgId);

            } else {
                log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause);
            }
        });

        // 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调
        rabbitTemplate.setMandatory(true);
        // 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);
        });

        return rabbitTemplate;
    }
    @Bean
    public Jackson2JsonMessageConverter converter() {
        return new Jackson2JsonMessageConverter();
    }
}

4、消息发送测试

@Component
public class MsgSender {
@Autowired
private RabbitTemplate rabbitTemplate;

//routingkey 路由键
@Value("${wx.oa.msg.mq.config.queue.routing.key}")
private String routingkey;


@Value("${app.rabbitmq.exchange.wx.oa}") String wxOAExchangeName;
/*
 * 发送消息的方法
 */
public void send(WxOfficialAccountsMqMsg msg){
    /**
     * convertAndSend - 转换并发送消息的template方法。
     * 是将传入的普通java对象,转换为rabbitmq中需要的message类型对象,并发送消息到rabbitmq中。
     * 参数一:交换器名称。 类型是String
     * 参数二:路由键。 类型是String
     * 参数三:消息,是要发送的消息内容对象。类型是Object
     */
    String id = UUID.randomUUID().toString();
    CorrelationData correlationData = new CorrelationData(id);
    msg.setBusinessMsgId(id);
    rabbitTemplate.convertAndSend(wxOAExchangeName, this.routingkey, msg,correlationData);

}

5、消息接收

@Slf4j
@Component

public class MessageListener {
    @Autowired
    private WxOfficialAccountsMsgSendService wxOfficialAccountsMsgSendService;

    @RabbitListener(queues = "${app.rabbitmq.queue.wx.oa}")
    public void consume(Message message, Channel channel) throws IOException {
        log.info("收到消息: {}", message.toString());
        WxOfficialAccountsMqMsg user= MessageHelper.msgToObj(message,WxOfficialAccountsMqMsg.class);
        //根据ID判断消费幂等性

        MessageProperties properties = message.getMessageProperties();
        long tag = properties.getDeliveryTag();
        boolean success = false;
        if (success) {
            // msgLogService.updateStatus(msgId, Constant.MsgLogStatus.CONSUMED_SUCCESS);
            channel.basicAck(tag, true);// 消费确认

        } else {
            //异常,ture 重新入队,或者false,进入死信队列
            channel.basicNack(tag, false, false);
        }

    }
}

public class MessageHelper {
    public static Message objToMsg(Object obj) {
        if (null == obj) {
            return null;
        }

        Message message = MessageBuilder.withBody(JsonUtil.objToStr(obj).getBytes()).build();
        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);// 消息持久化
        message.getMessageProperties().setContentType(MessageProperties.CONTENT_TYPE_JSON);

        return message;
    }

    public static <T> T msgToObj(Message message, Class<T> clazz) {
        if (null == message || null == clazz) {
            return null;
        }

        String str = new String(message.getBody());
        T obj = JsonUtil.strToObj(str, clazz);

        return obj;
    }
}

@Slf4j
public class JsonUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);
    private static ObjectMapper objectMapper = new ObjectMapper();
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 把Java对象转换成json字符串
     *
     * @param object 待转化为JSON字符串的Java对象
     * @return json 串 or null
     */
    public static String parseObjToJson(Object object) {
        String string = null;
        try {
            string = JSONObject.toJSONString(object);
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        return string;
    }

    /**
     * 将Json字符串信息转换成对应的Java对象
     *
     * @param json json字符串对象
     * @param c    对应的类型
     */
    public static <T> T parseJsonToObj(String json, Class<T> c) {
        try {
            JSONObject jsonObject = JSON.parseObject(json);
            return JSON.toJavaObject(jsonObject, c);
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        return null;
    }



    static {
        // 对象的所有字段全部列入
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        // 取消默认转换timestamps形式
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 忽略空bean转json的错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 统一日期格式
        objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
        // 忽略在json字符串中存在, 但在java对象中不存在对应属性的情况, 防止错误
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static <T> String objToStr(T obj) {
        if (null == obj) {
            return null;
        }

        try {
            return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("objToStr error: ", e);
            return null;
        }
    }

    public static <T> T strToObj(String str, Class<T> clazz) {
        if (StringUtils.isBlank(str) || null == clazz) {
            return null;
        }

        try {
            return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
        } catch (Exception e) {
            log.warn("strToObj error: ", e);
            return null;
        }
    }

    public static <T> T strToObj(String str, TypeReference<T> typeReference) {
        if (StringUtils.isBlank(str) || null == typeReference) {
            return null;
        }

        try {
            return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
        } catch (Exception e) {
            log.error("strToObj error", e);
            return null;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值