第四十一章: 基於SpringBoot & RabbitMQ完成DirectExchange分散式訊息消費

恆宇少年發表於2017-12-03

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

本章目標

基於SpringBoot專案整合RabbitMQ訊息佇列,完成DirectExchange(路由鍵)分散式訊息消費。

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下載最新的安裝包,因為是國外的網站所以下載比較慢,不過沒有關係,我再本章原始碼的resource目錄下存放了安裝包,本章原始碼在文章底部。

  2. 我們訪問RabiitmQ官方下載地址https://www.rabbitmq.com/download.html下載最新安裝包,該安裝包同樣存放在resource目錄下。

  3. 執行安裝Erlang

  4. 執行安裝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/sbin目錄下,下面我們需要手動設定環境變數,來支援服務執行,修改.profile配置檔案並新增如下配置:

PATH=$PATH:/usr/local/sbin
複製程式碼

配置完成後,可以直接通過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配套原始碼地址:gitee.com/hengboy/spr… SpringCloud配套原始碼地址:gitee.com/hengboy/spr… SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄 QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄 SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄 SpringBoot相關文章請訪問:目錄:SpringBoot學習目錄,感謝閱讀! 歡迎加入QQ技術交流群,共同進步。

QQ技術交流群

相關文章