在上一章第四十一章: 基於SpringBoot & RabbitMQ完成DirectExchange分散式訊息消費我們講解到了RabbitMQ
訊息佇列的DirectExchange
路由鍵訊息單個消費者消費,原始碼請訪問SpringBoot對應章節原始碼下載檢視,訊息佇列目的是完成訊息的分散式消費,那麼我們是否可以為一個Provider
建立並繫結多個Consumer
呢?
本章目標
基於SpringBoot
平臺整合RabbitMQ
訊息佇列,完成一個Provider
繫結多個Consumer
進行訊息消費。
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核心技術 |
構建專案
我們基於上一章的專案進行升級,我們先來將Chapter41
專案Copy
一份命名為Chapter42
。
構建 rabbitmq-consumer-node2
基於我們複製的Chapter42
專案,建立一個Module
子專案命名為rabbitmq-consumer-node2
,用於消費者的第二個節點,接下來我們為rabbitmq-consumer-node2
專案建立一個入口啟動類RabbitmqConsumerNode2Application
,程式碼如下所示:
/**
* 訊息佇列訊息消費者節點2入口
* ========================
*
* @author 恆宇少年
* Created with IntelliJ IDEA.
* Date:2017/11/26
* Time:15:15
* 碼雲:http://git.oschina.net/jnyqy
* ========================
*/
@SpringBootApplication
public class RabbitmqConsumerNode2Application
{
static Logger logger = LoggerFactory.getLogger(RabbitmqConsumerNode2Application.class);
/**
* rabbitmq消費者啟動入口
* @param args
*/
public static void main(String[] args)
{
SpringApplication.run(RabbitmqConsumerNode2Application.class,args);
logger.info("【【【【【訊息佇列-訊息消費者節點2啟動成功.】】】】】");
}
}
複製程式碼
為了區分具體的消費者節點,我們在專案啟動成功後列印了相關的日誌資訊,下面我們來編寫application.properties
配置檔案資訊,可以直接從rabbitmq-consumer
子專案內複製內容,複製後需要修改server.port
以及spring.application.name
,如下所示:
#埠號
server.port=1112
#專案名稱
spring.application.name=rabbitmq-consumer-node2
#rabbitmq相關配置
#使用者名稱
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-consumer
子專案內的UserConsumer
類到rabbitmq-consumer-node2
子專案對應的package
內,如下所示:
/**
* 使用者註冊訊息消費者
* 分散式節點2
* ========================
*
* @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 {
/**
* logback
*/
private Logger logger = LoggerFactory.getLogger(UserConsumer.class);
@RabbitHandler
public void execute(Long userId)
{
logger.info("使用者註冊消費者【節點2】獲取訊息,使用者編號:{}",userId);
//...//自行業務邏輯處理
}
}
複製程式碼
為了區分具體的消費者輸出內容,我們在上面UserConsumer
消費者消費方法內列印了相關日誌輸出,下面我們同樣把rabbitmq-consumer
子專案內UserConsumer
的消費方法寫入相關日誌,如下所示:
@RabbitHandler
public void execute(Long userId)
{
logger.info("使用者註冊消費者【節點1】獲取訊息,使用者編號:{}",userId);
//...//自行業務邏輯處理
}
複製程式碼
到目前為止我們的多節點
RabbitMQ
消費者已經編寫完成,下面我們來模擬多個使用者註冊的場景,來檢視使用者註冊訊息是否被轉發並唯一性的分配給不同的消費者節點。
執行測試
我們開啟上一章編寫的UserTester
測試類,為了模擬多使用者註冊請求,我們對應的建立一個內部執行緒類BatchRabbitTester
,線上程類內編寫註冊請求程式碼,如下所示:
/**
* 批量新增使用者執行緒測試類
* run方法傳送使用者註冊請求
*/
class BatchRabbitTester implements Runnable
{
private int index;
public BatchRabbitTester() { }
public BatchRabbitTester(int index) {
this.index = index;
}
@Override
public void run() {
try {
mockMvc.perform(MockMvcRequestBuilders.post("/user/save")
.param("userName","yuqiyu" + index)
.param("name","恆宇少年" + index)
.param("age","23")
)
.andDo(MockMvcResultHandlers.log())
.andReturn();
}catch (Exception e){
e.printStackTrace();
}
}
}
複製程式碼
為了區分每一個註冊資訊是否都已經寫入到資料庫,我們為BatchRabbitTester
新增了一個有參的構造方法,將for迴圈的i
值對應的傳遞為index
的值。下面我們來編寫對應的批量註冊的測試方法,如下所示:
/**
* 測試使用者批量新增
* @throws Exception
*/
@Test
public void testBatchUserAdd() throws Exception
{
for (int i = 0 ; i < 10 ; i++) {
//建立使用者註冊執行緒
Thread thread = new Thread(new BatchRabbitTester(i));
//啟動執行緒
thread.start();
}
//等待執行緒執行完成
Thread.sleep(2000);
}
複製程式碼
我們迴圈10次來測試使用者註冊請求,每一次都會建立一個執行緒去完成傳送註冊請求邏輯,在方法底部新增了sleep
方法,目的是為了阻塞測試用例的結束,因為我們測試使用者完成方法後會自動停止,不會去等待其他執行緒執行完成,所以這裡我們阻塞測試主執行緒來完成傳送註冊執行緒請求邏輯。
執行批量註冊測試方法
我們在執行測試批量註冊使用者訊息之前,先把rabbitmq-consumer
、rabbitmq-consumer-node2
兩個消費者子專案啟動,專案啟動完成後可以看到控制檯輸出啟動成功日誌,如下所示:
rabbitmq-consumer:
2017-12-10 17:10:36.961 INFO 15644 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 1111 (http)
2017-12-10 17:10:36.964 INFO 15644 --- [ main] c.h.r.c.RabbitmqConsumerApplication : Started RabbitmqConsumerApplication in 2.405 seconds (JVM running for 3.39)
2017-12-10 17:10:36.964 INFO 15644 --- [ main] c.h.r.c.RabbitmqConsumerApplication : 【【【【【訊息佇列-訊息消費者啟動成功.】】】】】
rabbitmq-consumer-node2:
2017-12-10 17:11:31.679 INFO 13812 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 1112 (http)
2017-12-10 17:11:31.682 INFO 13812 --- [ main] c.h.c.RabbitmqConsumerNode2Application : Started RabbitmqConsumerNode2Application in 2.419 seconds (JVM running for 3.129)
2017-12-10 17:11:31.682 INFO 13812 --- [ main] c.h.c.RabbitmqConsumerNode2Application : 【【【【【訊息佇列-訊息消費者節點2啟動成功.】】】】】
複製程式碼
接下來我們來執行testBatchUserAdd
方法,檢視測試控制檯輸出內容如下所示:
2017-12-10 17:15:02.619 INFO 14456 --- [ Thread-3] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#528df369:0/SimpleConnection@39b6ba57 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 60936]
回撥id:194b5e67-6913-474a-b2ac-6e938e1e85e8
訊息傳送成功
回撥id:e88ce59c-3eb9-433c-9e25-9429e7076fbe
訊息傳送成功
回撥id:3e5b8382-6f63-450f-a641-e3d8eee255b2
訊息傳送成功
回撥id:39103357-6c80-4561-acb7-79b32d6171c9
訊息傳送成功
回撥id:9795d227-b54e-4cde-9993-a5b880fcfe39
訊息傳送成功
回撥id:e9b8b828-f069-455f-a366-380bf10a5909
訊息傳送成功
回撥id:6b5b4a9c-5e7f-4c53-9eef-98e06f8be867
訊息傳送成功
回撥id:619a42f3-cb94-4434-9c75-1e28a04ce350
訊息傳送成功
回撥id:6b720465-b64a-4ed9-9d8c-3e4dafa4faed
訊息傳送成功
回撥id:b4296f7f-98cc-423b-a4ef-0fc31d22cb08
訊息傳送成功
複製程式碼
可以看到確實已經成功的傳送了10條使用者註冊訊息到RabbitMQ
服務端,那麼是否已經正確的成功的將訊息轉發到消費者監聽方法了呢?我們來開啟rabbitmq-consumer
子專案的啟動控制檯檢視日誌輸出內容如下所示:
2017-12-10 17:10:36.964 INFO 15644 --- [ main] c.h.r.c.RabbitmqConsumerApplication : 【【【【【訊息佇列-訊息消費者啟動成功.】】】】】
2017-12-10 17:15:02.695 INFO 15644 --- [cTaskExecutor-1] c.h.rabbitmq.consumer.user.UserConsumer : 使用者註冊消費者【節點1】獲取訊息,使用者編號:20
2017-12-10 17:15:02.718 INFO 15644 --- [cTaskExecutor-1] c.h.rabbitmq.consumer.user.UserConsumer : 使用者註冊消費者【節點1】獲取訊息,使用者編號:22
2017-12-10 17:15:02.726 INFO 15644 --- [cTaskExecutor-1] c.h.rabbitmq.consumer.user.UserConsumer : 使用者註冊消費者【節點1】獲取訊息,使用者編號:26
2017-12-10 17:15:02.729 INFO 15644 --- [cTaskExecutor-1] c.h.rabbitmq.consumer.user.UserConsumer : 使用者註冊消費者【節點1】獲取訊息,使用者編號:21
2017-12-10 17:15:02.789 INFO 15644 --- [cTaskExecutor-1] c.h.rabbitmq.consumer.user.UserConsumer : 使用者註冊消費者【節點1】獲取訊息,使用者編號:28
複製程式碼
可以看到成功的接受了5
條對應使用者註冊訊息內容,不過這裡具體接受的條數並不是固定的,這也是RabbitMQ
訊息轉發權重內部問題。
下面我們開啟rabbitmq-consumer-node2
子專案控制檯檢視日誌輸出內容如下所示:
2017-12-10 17:11:31.682 INFO 13812 --- [ main] c.h.c.RabbitmqConsumerNode2Application : 【【【【【訊息佇列-訊息消費者節點2啟動成功.】】】】】
2017-12-10 17:15:02.708 INFO 13812 --- [cTaskExecutor-1] com.hengyu.consumer.user.UserConsumer : 使用者註冊消費者【節點2】獲取訊息,使用者編號:25
2017-12-10 17:15:02.717 INFO 13812 --- [cTaskExecutor-1] com.hengyu.consumer.user.UserConsumer : 使用者註冊消費者【節點2】獲取訊息,使用者編號:23
2017-12-10 17:15:02.719 INFO 13812 --- [cTaskExecutor-1] com.hengyu.consumer.user.UserConsumer : 使用者註冊消費者【節點2】獲取訊息,使用者編號:24
2017-12-10 17:15:02.727 INFO 13812 --- [cTaskExecutor-1] com.hengyu.consumer.user.UserConsumer : 使用者註冊消費者【節點2】獲取訊息,使用者編號:27
2017-12-10 17:15:02.790 INFO 13812 --- [cTaskExecutor-1] com.hengyu.consumer.user.UserConsumer : 使用者註冊消費者【節點2】獲取訊息,使用者編號:29
複製程式碼
同樣獲得了5
條使用者註冊訊息,不過並沒有任何規律可言,編號也不是順序的。
所以多節點時訊息具體分發到哪個節點並不是固定的,完全是
RabbitMQ
分發機制來控制。
總結
本章完成了基於SpringBoot
平臺整合RabbitMQ
單個Provider
對應繫結多個Consumer
來進行多節點分散式消費者訊息消費,實際生產專案部署時完全可以將消費節點分開到不同的伺服器,只要消費節點可以訪問到RabbitMQ
服務端,可以正常通訊,就可以完成訊息消費。
本章原始碼已經上傳到碼雲: SpringBoot配套原始碼地址:gitee.com/hengboy/spr… SpringCloud配套原始碼地址:gitee.com/hengboy/spr… SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄 QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄 SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄,感謝閱讀! 歡迎加入QQ技術交流群,共同進步。