消息隊列目前流行的有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
路由鍵方式,根據設置的路由鍵的值進行完全匹配時轉發,下面我們來看一張圖,形象的介紹了轉發消息匹配流程,如下圖所示:
我們可以看到上圖,當消息被提供者發送到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
。
我們通過
Erlang
官方網站http://www.erlang.org/downloads
下載最新的安裝包我們訪問
RabiitmQ
官方下載地址https://www.rabbitmq.com/download.html
下載最新安裝包。
因為是國外的網站所以下載比較慢,大家下載時會浪費時間,我已經將安裝包分享到了百度網盤,下載地址:安裝包下載地址,密碼:
pexf
運行安裝
Erlang
運行安裝
RabbitMQ
5.檢查服務是否安裝完成,RabbitMQ
安裝完成后會以服務的形式創建,并且隨著開機啟動,如下所示:
Mac OS X 安裝
在Mac OS X中我們使用brew
工具可以很簡單的安裝RabbitMQ
服務端,步驟如下:
-
brew
更新到最新版本,執行:brew update
- 接下來我們安裝
Erlang
,執行:brew install erlang
- 最后安裝
RabbitMQ
,執行:brew install rabbitmq
我們通過上面的步驟安裝后,RabbitMQ
會被自動安裝到/usr/local/Cellar/rabbitmq/
目錄下,下面我們進入cd sbin
目錄執行:
sudo ./rabbitmq-server
可以直接啟動RabbitMQ
服務。
Ubuntu 安裝
在Ubuntu
操作系統中,我們可以直接使用APT
倉庫進行安裝,我使用的系統版本是16.04
,系統版本并不影響安裝。
- 安裝
Erlang
,執行命令:sudo apt-get install erlang
- 下面我們需要將
RabbitMQ
的安裝源配置信息寫入到系統的/etc/apt/sources.list.d
配置文件內,執行如下命令:
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
- 下面我們更新
APT
本地倉庫的安裝包列表,執行命令:sudo apt-get update
- 最后安裝
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