第四十一章: 基于SpringBoot & RabbitMQ完成DirectExchange分布式消息消費

消息隊列目前流行的有KafKa、RabbitMQ、ActiveMQ等,它們的誕生無非不是為了解決消息的分布式消費,完成項目、服務之間的解耦動作。消息隊列提供者與消費者之間完全采用異步通信方式,極力的提高了系統的響應能力,從而提高系統的網絡請求吞吐量。
每一種的消息隊列都有它在設計上的獨一無二的優勢,在實際的項目技術選型時根據項目的需求來確定。

免費教程專題

恒宇少年在博客整理三套免費學習教程專題,由于文章偏多特意添加了閱讀指南,新文章以及之前的文章都會在專題內陸續填充,希望可以幫助大家解惑更多知識點。

本章目標

基于SpringBoot項目整合RabbitMQ消息隊列,完成DirectExchange(路由鍵)分布式消息消費。

SpringBoot 企業級核心技術學習專題


專題 專題名稱 專題描述
001 Spring Boot 核心技術 講解SpringBoot一些企業級層面的核心組件
002 Spring Boot 核心技術章節源碼 Spring Boot 核心技術簡書每一篇文章碼云對應源碼
003 Spring Cloud 核心技術 對Spring Cloud核心技術全面講解
004 Spring Cloud 核心技術章節源碼 Spring Cloud 核心技術簡書每一篇文章對應源碼
005 QueryDSL 核心技術 全面講解QueryDSL核心技術以及基于SpringBoot整合SpringDataJPA
006 SpringDataJPA 核心技術 全面講解SpringDataJPA核心技術
007 SpringBoot核心技術學習目錄 SpringBoot系統的學習目錄,敬請關注點贊!!!

Exchange

RabbitMQ中有三種常用的轉發方式,分別是:

DirectExchange:路由鍵方式轉發消息。
FanoutExchange:廣播方式轉發消息。
TopicExchange:主題匹配方式轉發消息。

我們本章先來講解DirectExchange路由鍵方式,根據設置的路由鍵的值進行完全匹配時轉發,下面我們來看一張圖,形象的介紹了轉發消息匹配流程,如下圖所示:

DirectExchange

我們可以看到上圖,當消息被提供者發送到RabbitMQ后,會根據配置隊列的交換以及綁定實例進行轉發消息,上圖只會將消息轉發路由鍵為KEY的隊列消費者對應的實現方法邏輯中,從而完成消息的消費過程。

安裝RabbitMQ

因為RabbitMQ是跨平臺的分布式消息隊列服務,可以部署在任意的操作系統上,下面我們分別介紹在不同的系統下該怎么去安裝RabbitMQ服務。

我們本章采用的環境版本如下:

  • RabbitMQ Server 3.6.14
  • Erlang/OTP_X64 20.1

Windows下安裝

我們先去RabbitMQ官方網站下載最新版的安裝包,下載地址:https://www.rabbitmq.com/download.html,可以根據不同的操作系統選擇下載。
我們在安裝RabbitMQ服務端時需要Erlang環境的支持,所以我們需要先安裝Erlang

  1. 我們通過Erlang官方網站http://www.erlang.org/downloads下載最新的安裝包

  2. 我們訪問RabiitmQ官方下載地址https://www.rabbitmq.com/download.html下載最新安裝包。

因為是國外的網站所以下載比較慢,大家下載時會浪費時間,我已經將安裝包分享到了百度網盤,下載地址:安裝包下載地址,密碼:pexf

  1. 運行安裝Erlang

  2. 運行安裝RabbitMQ

5.檢查服務是否安裝完成,RabbitMQ安裝完成后會以服務的形式創建,并且隨著開機啟動,如下所示:

Rabbit服務

Mac OS X 安裝

在Mac OS X中我們使用brew工具可以很簡單的安裝RabbitMQ服務端,步驟如下:

  1. brew更新到最新版本,執行:brew update
  2. 接下來我們安裝Erlang,執行:brew install erlang
  3. 最后安裝RabbitMQ,執行:brew install rabbitmq

我們通過上面的步驟安裝后,RabbitMQ會被自動安裝到/usr/local/Cellar/rabbitmq/目錄下,下面我們進入cd sbin目錄執行:

sudo ./rabbitmq-server

可以直接啟動RabbitMQ服務。

Ubuntu 安裝

Ubuntu操作系統中,我們可以直接使用APT倉庫進行安裝,我使用的系統版本是16.04,系統版本并不影響安裝。

  1. 安裝Erlang,執行命令:sudo apt-get install erlang
  2. 下面我們需要將RabbitMQ的安裝源配置信息寫入到系統的/etc/apt/sources.list.d配置文件內,執行如下命令:
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
  1. 下面我們更新APT本地倉庫的安裝包列表,執行命令:sudo apt-get update
  2. 最后安裝RabbitMQ服務,執行命令:sudo apt-get install rabbitmq-server

啟用界面管理插件

RabbitMQ提供了界面管理的web插件,我們只需要啟用指定的插件就可以了,下面我們來看看Windows操作系統下該怎么啟動界面管理插件。
我們使用CMD進入RabbitMQ安裝目錄C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.14,然后我們進入sbin目錄,可以看到目錄內存在很多個bat腳本程序,我們找到rabbitmq-plugins.bat,這個腳本程序可以控制RabbitMQ插件啟用禁用,我們執行如下腳本命令來啟用界面管理插件:

rabbitmq-plugins.bat enable rabbitmq_management

命令行輸出內容如下所示:

The following plugins have been enabled:
  amqp_client
  cowlib
  cowboy
  rabbitmq_web_dispatch
  rabbitmq_management_agent
  rabbitmq_management

Applying plugin configuration to rabbit@yuqiyu... started 6 plugins.

可以看到輸出的內容RabbitMQ自動啟動了6個插件,我們現在訪問http://127.0.0.1:15672地址可以直接打開RabbitMQ的界面管理平臺,而默認的用戶名/密碼分別為:guest/guest,通過該用戶可以直接登錄管理平臺。

禁用界面管理插件

我們同樣可以禁用RabbitMQ指定插件,執行如下命令:

rabbitmq-plugins.bat disable rabbitmq_management

命令創建輸出內容則是相關停止插件的日志,如下:

The following plugins have been disabled:
  amqp_client
  cowlib
  cowboy
  rabbitmq_web_dispatch
  rabbitmq_management_agent
  rabbitmq_management

Applying plugin configuration to rabbit@yuqiyu... stopped 6 plugins.

這樣我們再訪問http://127.0.0.1:15672就會發現我們無法訪問到界面。

構建項目

我們使用idea開發工具創建一個SpringBoot項目,添加依賴,pom.xml配置文件如下所示:

<dependencies>
        <!--rabbitmq依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--web依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--lombok依賴-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--fastjson依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.40</version>
        </dependency>
        <!--測試依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

我們本章來模擬用戶注冊完成后,將注冊用戶的編號通過Provider模塊發送到RabbitMQ,然后RabbitMQ根據配置的DirectExchange的路由鍵進行異步轉發。

初始化用戶表

下面我們先來創建所需要的用戶基本信息表,建表SQL如下所示:

CREATE TABLE `user_info` (
  `UI_ID` int(11) DEFAULT NULL COMMENT '用戶編號',
  `UI_USER_NAME` varchar(20) DEFAULT NULL COMMENT '用戶名稱',
  `UI_NAME` varchar(20) DEFAULT NULL COMMENT '真實姓名',
  `UI_AGE` int(11) DEFAULT NULL COMMENT '用戶年齡',
  `UI_BALANCE` decimal(10,0) DEFAULT NULL COMMENT '用戶余額'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶基本信息表';

構建 rabbitmq-provider 項目

基于我們上述的項目創建一個Maven子模塊,命名為:rabbitmq-provider,因為是直接創建的Module項目,IDEA并沒有給我創建SpringApplication啟用類。

創建入口類

下面我們自行創建一個Provider項目啟動入口程序,如下所示:

/**
 * 消息隊列消息提供者啟動入口
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:14
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@SpringBootApplication
public class RabbitmqProviderApplication
{
    static Logger logger = LoggerFactory.getLogger(RabbitmqProviderApplication.class);

    /**
     * 消息隊列提供者啟動入口
     * @param args
     */
    public static void main(String[] args)
    {
        SpringApplication.run(RabbitmqProviderApplication.class,args);

        logger.info("【【【【【消息隊列-消息提供者啟動成功.】】】】】");
    }
}
application.properties配置文件

下面我們在src/main/resource目錄下創建application.properties并將對應RabbitMQ以及Druid的配置加入,如下所示:

#用戶名
spring.rabbitmq.username=guest
#密碼
spring.rabbitmq.password=guest
#服務器ip
spring.rabbitmq.host=localhost
#虛擬空間地址
spring.rabbitmq.virtual-host=/
#端口號
spring.rabbitmq.port=5672
#配置發布消息確認回調
spring.rabbitmq.publisher-confirms=true

#數據源配置
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.druid.username=root
spring.datasource.druid.password=123456

RabbitMQ內有個virtual-host即虛擬主機的概念,一個RabbitMQ服務可以配置多個虛擬主機,每一個虛擬機主機之間是相互隔離,相互獨立的,授權用戶到指定的virtual-host就可以發送消息到指定隊列。

用戶實體

本章數據庫操作采用spring-data-jpa,相關文章請訪問:第十三章:SpringBoot實戰SpringDataJPA,我們基于user_info數據表對應創建實體,如下所示:

@Data
@Table(name = "user_info")
@Entity
public class UserEntity
    implements Serializable
{
    /**
     * 用戶編號
     */
    @Id
    @GeneratedValue
    @Column(name = "UI_ID")
    private Long id;
    /**
     * 用戶名稱
     */
    @Column(name = "UI_USER_NAME")
    private String userName;
    /**
     * 姓名
     */
    @Column(name = "UI_NAME")
    private String name;
    /**
     * 年齡
     */
    @Column(name = "UI_AGE")
    private int age;
    /**
     * 余額
     */
    @Column(name = "UI_BALANCE")
    private BigDecimal balance;
}
用戶數據接口

創建UserRepository用戶數據操作接口,并繼承JpaRepository獲得spring-data-jpa相關的接口定義方法。如下所示:

/**
 * 用戶數據接口定義
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:35
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
public interface UserRepository
    extends JpaRepository<UserEntity,Long>
{
}
用戶業務邏輯實現

本章只是簡單完成了數據的添加,代碼如下所示:

/**
 * 用戶業務邏輯實現類
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:37
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService
{
    @Autowired
    private UserRepository userRepository;
    /**
     * 消息隊列業務邏輯實現
     */
    @Autowired
    private QueueMessageService queueMessageService;

    /**
     * 保存用戶
     * 并寫入消息隊列
     * @param userEntity
     * @return
     */
    public Long save(UserEntity userEntity) throws Exception
    {
        /**
         * 保存用戶
         */
        userRepository.save(userEntity);
        /**
         * 將消息寫入消息隊列
         */
        queueMessageService.send(userEntity.getId(), ExchangeEnum.USER_REGISTER, QueueEnum.USER_REGISTER);

        return userEntity.getId();
    }

在上面業務邏輯實現類內出現了一個名為QueueMessageService消息隊列實現類,該類是我們定義的用于發送消息到消息隊列的統一入口,在下面我們會詳細講解。

用戶控制器

創建一個名為UserController的控制器類,對應編寫一個添加用戶的請求方法,如下所示:

/**
 * 用戶控制器
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:41
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
@RequestMapping(value = "/user")
public class UserController
{
    /**
     * 用戶業務邏輯
     */
    @Autowired
    private UserService userService;

    /**
     * 保存用戶基本信息
     * @param userEntity
     * @return
     */
    @RequestMapping(value = "/save")
    public UserEntity save(UserEntity userEntity) throws Exception
    {
        userService.save(userEntity);
        return userEntity;
    }
}

到這我們添加用戶的流程已經編寫完成了,那么我們就來看下消息隊列QueueMessageService接口的定義以及實現類的定義。

消息隊列方法定義接口

創建一個名為QueueMessageService的接口并且繼承了RabbitTemplate.ConfirmCallback接口,而RabbitTemplate.ConfirmCallback接口是用來回調消息發送成功后的方法,當一個消息被成功寫入到RabbitMQ服務端時,就會自動的回調RabbitTemplate.ConfirmCallback接口內的confirm方法完成通知,QueueMessageService接口如下所示:

/**
 * 消息隊列業務
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:50
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
public interface QueueMessageService
    extends RabbitTemplate.ConfirmCallback
{
    /**
     * 發送消息到rabbitmq消息隊列
     * @param message 消息內容
     * @param exchangeEnum 交換配置枚舉
     * @param queueEnum 隊列配置枚舉
     * @throws Exception
     */
    public void send(Object message, ExchangeEnum exchangeEnum, QueueEnum queueEnum) throws Exception;
}

接下來我們需要實現該接口內的所有方法,并做出一些業務邏輯的處理。

消息隊列業務實現

創建名為QueueMessageServiceSupport實體類實現QueueMessageService接口,并實現接口內的所有方法,如下所示:

/**
 * 消息隊列業務邏輯實現
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:52
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
public class QueueMessageServiceSupport
    implements QueueMessageService
{
    /**
     * 消息隊列模板
     */
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void send(Object message, ExchangeEnum exchangeEnum, QueueEnum queueEnum) throws Exception {
        //設置回調為當前類對象
        rabbitTemplate.setConfirmCallback(this);
        //構建回調id為uuid
        CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
        //發送消息到消息隊列
        rabbitTemplate.convertAndSend(exchangeEnum.getValue(),queueEnum.getRoutingKey(),message,correlationId);
    }

    /**
     * 消息回調確認方法
     * @param correlationData 請求數據對象
     * @param ack 是否發送成功
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println(" 回調id:" + correlationData.getId());
        if (ack) {
            System.out.println("消息發送成功");
        } else {
            System.out.println("消息發送失敗:" + cause);
        }
    }
}

convertAndSend方法用于將Object類型的消息轉換后發送到RabbitMQ服務端,發送是的消息類型要與消息消費者方法參數保持一致。

confirm方法內,我們僅僅打印了消息發送時的id,根據ack參數輸出消息發送狀態。

在上面代碼中我們注入了RabbitTemplate消息隊列模板實例,而通過該實例我們可以將消息發送到RabbitMQ服務端。那么這個實例具體在什么地方定義的呢?我們帶著這個疑問來創建下面的模塊,我們需要將RabbitMQ相關的配置抽取出來作為一個單獨的Module存在。

構建 rabbitmq-common 項目

該模塊項目很簡單,只是添加RabbitMQ相關的配置信息,由于Module是一個子模塊所以繼承了parent所有的依賴,當然我們用到的RabbitMQ相關依賴也不例外。

配置rabbitmq

在創建配置類之前,我們先來定義兩個枚舉,分別存放了隊列的交換信息、隊列路由信息,

  • ExchangeEnum (存放了隊列交換配置信息)
/**
 * rabbitmq交換配置枚舉
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:13:56
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Getter
public enum ExchangeEnum
{
    /**
     * 用戶注冊交換配置枚舉
     */
    USER_REGISTER("user.register.topic.exchange")
    ;
    private String value;

    ExchangeEnum(String value) {
        this.value = value;
    }
}
  • QueueEnum (存放了隊列信息以及隊列的路由配置信息)
/**
 * 隊列配置枚舉
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:14:05
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Getter
public enum QueueEnum
{
    /**
     * 用戶注冊枚舉
     */
    USER_REGISTER("user.register.queue","user.register")
    ;
    /**
     * 隊列名稱
     */
    private String name;
    /**
     * 隊列路由鍵
     */
    private String routingKey;

    QueueEnum(String name, String routingKey) {
        this.name = name;
        this.routingKey = routingKey;
    }
}

創建名為UserRegisterQueueConfiguration的實體類用于配置本章用到的用戶注冊隊列信息,如果你得項目中使用多個隊列,建議每一個業務邏輯創建一個配置類,分開維護,這樣不容易出錯。配置信息如下:

/**
 * 用戶注冊消息隊列配置
 * ========================
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:16:58
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration
public class UserRegisterQueueConfiguration {
    /**
     * 配置路由交換對象實例
     * @return
     */
    @Bean
    public DirectExchange userRegisterDirectExchange()
    {
        return new DirectExchange(ExchangeEnum.USER_REGISTER.getValue());
    }

    /**
     * 配置用戶注冊隊列對象實例
     * 并設置持久化隊列
     * @return
     */
    @Bean
    public Queue userRegisterQueue()
    {
        return new Queue(QueueEnum.USER_REGISTER.getName(),true);
    }

    /**
     * 將用戶注冊隊列綁定到路由交換配置上并設置指定路由鍵進行轉發
     * @return
     */
    @Bean
    public Binding userRegisterBinding()
    {
        return BindingBuilder.bind(userRegisterQueue()).to(userRegisterDirectExchange()).with(QueueEnum.USER_REGISTER.getRoutingKey());
    }
}

該配置類大致分為如下三部分:

  • 配置交換實例
    配置DirectExchange實例對象,為交換設置一個名稱,引用ExchangeEnum枚舉配置的交換名稱,消息提供者與消息消費者的交換名稱必須一致才具備的第一步的通訊基礎。

  • 配置隊列實例
    配置Queue實例對象,為消息隊列設置一個名稱,引用QueueEnum枚舉配置的隊列名稱,當然隊列的名稱同樣也是提供者與消費者之間的通訊基礎。

  • 綁定隊列實例到交換實例
    配置Binding實例對象,消息綁定的目的就是將Queue實例綁定到Exchange上,并且通過設置的路由Key進行消息轉發,配置了路由Key后,只有符合該路由配置的消息才會被轉發到綁定交換上的消息隊列。

我們的rabbitmq-common模塊已經編寫完成。

添加 rabbitmq-provider 依賴 rabbitmq-common

下面我們回到rabbitmq-provider模塊,修改pom.xml配置文件,如下所示:

<dependencies>
        <!--添加common模塊依賴-->
        <dependency>
            <groupId>com.hengyu</groupId>
            <artifactId>rabbitmq-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--mysql依賴-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid數據源依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.5</version>
        </dependency>
        <!--data jpa依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
    </dependencies>

可以看到我們將rabbitmq-common模塊添加到了rabbitmq-provider模塊的pom配置文件內,完成了模塊之間的相互依賴,這樣我們rabbitmq-provider就自動添加了對應的消息隊列配置。

構建rabbitmq-consumer

我們再來創建一個rabbitmq-consumer隊列消息消費者模塊,用于接受消費用戶注冊消息。

創建入口類

同樣我們先來創建一個SpringApplication入口啟動類,如下所示:

/**
 * 消息隊列消息消費者入口
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:15:15
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@SpringBootApplication
public class RabbitmqConsumerApplication
{
    static Logger logger = LoggerFactory.getLogger(RabbitmqConsumerApplication.class);

    /**
     * rabbitmq消費者啟動入口
     * @param args
     */
    public static void main(String[] args)
    {
        SpringApplication.run(RabbitmqConsumerApplication.class,args);

        logger.info("【【【【【消息隊列-消息消費者啟動成功.】】】】】");
    }
}
application.properties配置文件

配置文件的消息隊列配置信息要與rabbitmq-provider配置文件一致,如下所示:

spring.application.name=rabbitmq-consumer
#啟動端口
server.port=1111
#用戶名
spring.rabbitmq.username=guest
#密碼
spring.rabbitmq.password=guest
#服務器ip
spring.rabbitmq.host=localhost
#虛擬空間地址
spring.rabbitmq.virtual-host=/
#端口號
spring.rabbitmq.port=5672
#配置發布消息確認回調
spring.rabbitmq.publisher-confirms=true

我們修改了程序啟動的端口號,為了我們下面進行測試的時候不出現端口占用的情況。

如果RabbitMQ配置信息與rabbitmq-provider不一致,就不會收到消費消息。

用戶注冊消息消費者

創建名為UserConsumer類,用于完成消息監聽,并且實現消息消費,如下所示:

/**
 * 用戶注冊消息消費者
 * ========================
 *
 * @author 恒宇少年
 * Created with IntelliJ IDEA.
 * Date:2017/11/26
 * Time:15:20
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Component
@RabbitListener(queues = "user.register.queue")
public class UserConsumer {

    @RabbitHandler
    public void execute(Long userId)
    {
        System.out.println("用戶:" + userId+",完成了注冊");

        //...//自行業務邏輯處理
    }
}

在消息消費者類內,有兩個陌生的注解:

  • @RabbitListener
    RabbitMQ隊列消息監聽注解,該注解配置監聽queues內的隊列名稱列表,可以配置多個。隊列名稱對應本章rabbitmq-common模塊內QueueEnum枚舉name屬性。
  • @RabbitHandler
    RabbitMQ消息處理方法,該方法的參數要與rabbitmq-provider發送消息時的類型保持一致,否則無法自動調用消費方法,也就無法完成消息的消費。

運行測試

我們接下來在rabbitmq-provider模塊src/test/java下創建一個測試用例,訪問用戶注冊控制器請求路徑,如下所示:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitmqProviderApplication.class)
public class UserTester
{
    /**
     * 模擬mvc測試對象
     */
    private MockMvc mockMvc;

    /**
     * web項目上下文
     */
    @Autowired
    private WebApplicationContext webApplicationContext;

    /**
     * 所有測試方法執行之前執行該方法
     */
    @Before
    public void before() {
        //獲取mockmvc對象實例
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    /**
     * 測試添加用戶
     * @throws Exception
     */
    @Test
    public void testUserAdd() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.post("/user/save")
                .param("userName","yuqiyu")
                .param("name","恒宇少年")
                .param("age","23")
        )
                .andDo(MockMvcResultHandlers.log())
                .andReturn();
    }
}

調用測試用例時會自動將參數保存到數據庫,并且將用戶編號發送到RabbitMQ服務端,而RabbitMQ根據交換配置以及隊列配置轉發消息到消費者實例。

啟動 rabbitmq-consumer

我們先來把rabbitmq-consumer項目啟動,控制臺輸出啟動日志如下所示:

.....
51.194  INFO 2340 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'rabbitConnectionFactory' has been autodetected for JMX exposure
2017-12-03 16:58:51.196  INFO 2340 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'rabbitConnectionFactory': registering with JMX server as MBean [org.springframework.amqp.rabbit.connection:name=rabbitConnectionFactory,type=CachingConnectionFactory]
2017-12-03 16:58:51.216  INFO 2340 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2017-12-03 16:58:51.237  INFO 2340 --- [cTaskExecutor-1] o.s.a.r.c.CachingConnectionFactory       : Created new connection: rabbitConnectionFactory#443ff8ef:0/SimpleConnection@4369ac5c [delegate=amqp://guest@127.0.0.1:5672/, localPort= 62107]
2017-12-03 16:58:51.287  INFO 2340 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 1111 (http)
2017-12-03 16:58:51.290  INFO 2340 --- [           main] c.h.r.c.RabbitmqConsumerApplication      : Started RabbitmqConsumerApplication in 2.354 seconds (JVM running for 3.026)
2017-12-03 16:58:51.290  INFO 2340 --- [           main] c.h.r.c.RabbitmqConsumerApplication      : 【【【【【消息隊列-消息消費者啟動成功.】】】】】

該部分啟動日志就是我們配置的RabbitMQ初始化信息,我們可以看到項目啟動時會自動與配置的RabbitMQ進行關聯:

[delegate=amqp://guest@127.0.0.1:5672/, localPort= 62107]
運行測試用例

接下來我們執行rabbitmq-provider項目的測試用例,來查看控制臺的輸出內容如下所示:

......
 回調id:e08f6d82-57bc-4c3f-9899-31c4b990c5be
消息發送成功
......

已經可以正常的將消息發送到RabbitMQ服務端,并且接收到了回調通知,那么我們的rabbitmq-consumer項目是不是已經執行了消息的消費呢?我們打開rabbitmq-consumer控制臺查看輸出內容如下所示:

用戶:2,完成了注冊

看以看到已經可以成功的執行UserConsumer消息監聽類內的監聽方法邏輯,到這里消息隊列路由一對一的方式已經講解完了。

總結

本章主要講解了RabbitMQ在不同操作系統下的安裝方式,以及通過三個子模塊形象的展示了消息的分布式處理,整體流程:rabbitmq-provider -> RabbitMQ服務端 -> rabbitmq-consumer,消息的轉發是非常快的,RabbitMQ在收到消息后就會檢索當前服務端是否存在該消息的消費者,如果存在將會馬上將消息轉發。

本章源碼已經上傳到碼云:
SpringBoot配套源碼地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源碼地址:https://gitee.com/hengboy/spring-cloud-chapter

作者個人 博客
使用開源框架 ApiBoot 助你成為Api接口服務架構師

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,969評論 19 139
  • 關于消息隊列,從前年開始斷斷續續看了些資料,想寫很久了,但一直沒騰出空,近來分別碰到幾個朋友聊這塊的技術選型,是時...
    預流閱讀 585,475評論 51 786
  • 1.RabbitMQ概述 簡介: MQ全稱為Message Queue,消息隊列是應用程序和應用程序之間的通信方法...
    梁朋舉閱讀 50,282評論 0 47
  • RabbitMQ 原理介紹及安裝部署 標簽:RabbitMQ 安裝 簡介 RabbitMQ 是一個用 Erlang...
    神仙CGod閱讀 8,616評論 0 60
  • 來源 RabbitMQ是用Erlang實現的一個高并發高可靠AMQP消息隊列服務器。支持消息的持久化、事務、擁塞控...
    jiangmo閱讀 10,407評論 2 34