Rabbitmq整合springboot进行可靠消息发送接收

Rabbitmq整合springboot

rabbitmq整合springboot中,配置信息方面的整理

项目搭建

该项目为测试项目的搭建,总共分为四个模块,分别为:父项目、common模块、消息生产者模块、消息消费者模块。

在这里插入图片描述

父项目模块(rabbit-pro)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.feng.rabbit</groupId>
    <artifactId>rabbit</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>message-pro</module>
        <module>message-cus</module>
        <module>common</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.8</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.76</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

父项目模块基于springboot2.2.1.RELEASE版本,具体依赖信息,如上所示。

common模块

项目结构如图:

在这里插入图片描述

MsgObj:该对象是消息体,消息生产者模块和消息消费者模块都会用到该对象

package com.feng.rabbit.com.domain;

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

import java.io.Serializable;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 14:24
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MsgObj implements Serializable {

    private String msgId;

    private String name;

    private String addr;

    private int age;

    private String exchange;

    private String routeKey;

}

Constants:定义的消息队列名,交换机名,路由名

package com.feng.rabbit.com.utils;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 15:11
 **/
public interface Constants {
	// 队列名
    String QUEUE_NAME = "mail.queue1";
	// 交换机名
    String EXCHANGE_NAME = "mail.exchange1";
	// 路由名
    String MAIL_ROUTING_KEY = "mail.routing1";

}

消息生产者模块

项目结构图:

在这里插入图片描述

RabbitConfig:rabbitmq配置类

package com.feng.rabbit.pro.config;

import com.feng.rabbit.com.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 13:44
 **/
@Configuration
public class RabbitConfig {

    private final static Logger log = LoggerFactory.getLogger(RabbitConfig.class);
	
    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    @Bean
    RabbitTemplate rabbitTemplate () {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
        // 当消息发送到rabbitmq时,如果消息被rabbitmq收到,rabbitmq会回调下面的方法
        // 参数1:回调相关参数
        // 参数2:boolean值,true表示mq接受消息成功,false表示接受消息失败
        // 参数3:String类型,表示失败原因
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            String msgId = correlationData.getId();// 消息唯一ID
            if (ack)
                log.info("消息发送成功:{}", msgId);
            else
                log.warn("消息发送失败:{},失败原因:{}", msgId, cause);

        });
        // 当消息从交换机发送到对应队列失败时,触发该回调
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            log.warn("交换机发送消息到队列失败:{}", message);
            log.warn("交换机发送消息到队列失败replyCode:{}", replyCode);
            log.warn("交换机发送消息到队列失败replyText:{}", replyText);
            log.warn("交换机发送消息到队列失败exchange:{}", exchange);
            log.warn("交换机发送消息到队列失败routingKey:{}", routingKey);
        });

        return rabbitTemplate;
    }

    @Bean("mailQueue")
    public Queue mailQueue() {
        // 参数1:队列名   参数2:是否持久化
        return new Queue(Constants.QUEUE_NAME, true);
    }

    @Bean("mailExchange")
    public DirectExchange mailExchange() {
        // 参数1:交换机名     参数2:是否持久化   参数3:是否自动删除,如果没有与之绑定的Queue,直接删除  参数4:结构化参数
        return new DirectExchange(Constants.EXCHANGE_NAME, true, false, null);
    }

    @Bean
    public Binding mailBinding() {
        // 将queue和exchange进行绑定
        return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(Constants.MAIL_ROUTING_KEY);
    }

}

MailController:用于触发生产者发送消息的接口

package com.feng.rabbit.pro.controller;

import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import org.springframework.amqp.rabbit.connection.CorrelationData;
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.util.UUID;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 14:25
 **/
@RestController
@RequestMapping(value = "/mail")
public class MailController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping(value = "/sendQueue1")
    public String send() {
        String msgId = UUID.randomUUID().toString();
        MsgObj msgObj = new MsgObj(msgId, "阿木木", "四川成都",
                22, Constants.EXCHANGE_NAME, Constants.MAIL_ROUTING_KEY);
        // 参数1:交换机名     
        // 参数2:路由名   
        // 参数3:消息体     
        // 参数4:ConfirmCallback回调信息中的唯一ID
        rabbitTemplate.convertAndSend(Constants.EXCHANGE_NAME,
                Constants.MAIL_ROUTING_KEY,
                msgObj,
                new CorrelationData(msgId));
        return "success";
    }

}

application.yml:springboot配置信息

server:
  port: 8090

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    publisher-confirm-type: correlated #开启Rabbitmq发送消息确认机制,发送消息到队列并触发回调方法,  开启confirm回调
    publisher-returns: true #可以在消息没有被路由到指定的queue时将消息返回,而不是丢弃   开启returnedMessage回调
    # 监听端手动确认机制,只有在监听端配置才有效
#    listener:
#      simple:
#        acknowledge-mode: manual # 开启手动确认模式

消息消费者模块

项目结构:

在这里插入图片描述

MailListener:队列监听类,如果没有配置手动确认机制,则按照下列写法是没有问题

package com.feng.rabbit.cus.receiver;

import com.alibaba.fastjson.JSON;
import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 14:58
 **/
@Component
public class MailListener {

    private final static Logger log = LoggerFactory.getLogger(MailListener.class);

    @RabbitListener(queues = Constants.QUEUE_NAME)
    public void mailHandler(MsgObj msgObj) {
        log.info("接收到队列({})的消息为:{}", Constants.QUEUE_NAME, JSON.toJSONString(msgObj));
    }

}

如果配置了手动确认机制,需要按照如下写法:

package com.feng.rabbit.cus.receiver;

import com.alibaba.fastjson.JSON;
import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @description:
 * @author: fenglin
 * @create: 2021-08-13 14:58
 **/
@Component
public class MailListener {

    private final static Logger log = LoggerFactory.getLogger(MailListener.class);

    @RabbitListener(queues = Constants.QUEUE_NAME)
    public void mailHandler(MsgObj msgObj, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        log.info("接收到队列({})的消息为:{}", Constants.QUEUE_NAME, JSON.toJSONString(msgObj));
        try {
            channel.basicAck(tag, false); // 手动确认消息
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

application.yml:springboot配置文件

server:
  port: 8091

spring:
  rabbitmq:
    host: 192.168.71.128
    port: 5672
    username: guest
    password: guest
    # 监听端手动确认机制
    listener:
      simple:
        acknowledge-mode: manual # 开启手动确认模式

消费端是开启了手动确认模式的。
补充一点:开启手动确认模式和没有开启手动确认模式有什么区别呢?
如果没有开启手动确认模式,消息队列会直接将消息发给对应的监听,无需确认手动确认;
开启手动确认模式后,必须要手动执行channel.basicAck(tag, false),否则在下一次启动消费者端时,消息队列会再次把之前没有手动确认过的消息发送到监听器中,也就是说,消息堆积在消息队列中,极端情况是会将消息队列中的内存沾满,造成消息队列崩溃。
在这里插入图片描述
在rabbitmq客户端,通过对应的队列,就可以看到没有确认的消息数。

功能测试

通过浏览器访问http://localhost:8090/mail/sendQueue1

消息生产者:

在这里插入图片描述
注意:消息生产者打印出来的ID,这是我们发送消息时,设置的ID

String msgId = UUID.randomUUID().toString();
MsgObj msgObj = new MsgObj(msgId, "冯林", "四川南充",
                           22, Constants.EXCHANGE_NAME, Constants.MAIL_ROUTING_KEY);
// 参数1:交换机名     参数2: 路由名    参数3:消息体     参数4:ConfirmCallback回调信息中的唯一ID
rabbitTemplate.convertAndSend(Constants.EXCHANGE_NAME,
                              Constants.MAIL_ROUTING_KEY,
                              msgObj,
                              new CorrelationData(msgId));

消息消费者:
在这里插入图片描述
项目代码已上传到GitEE:https://gitee.com/fenglin676168/rabbitmq.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值