1. 概述
我們在上一篇系列文章中瞭解到,我們可以利用 Spring Remoting 以及相關的技術,在服務器和客户端之間通過 HTTP 渠道實現同步 遠程過程調用。
在本篇文章中,我們將 探索 Spring Remoting 在 AMQP 之上,從而實現通過一種本質上異步的媒介執行同步 RPC。
2. 安裝 RabbitMQ
有多種與 AMQP 兼容的消息傳遞系統可供選擇,我們選擇 RabbitMQ,因為它是一個經過驗證的平台,並且完全支持 Spring——這兩個產品均由同一家公司(Pivotal)管理。
如果您不熟悉 AMQP 或 RabbitMQ,您可以閲讀我們的快速介紹。
因此,第一步是安裝和啓動 RabbitMQ。安裝方法多種多樣,您可以選擇您喜歡的安裝方法,按照官方指南中的步驟進行 操作。
3. Maven 依賴項
我們將設置服務器端和客户端 Spring Boot 應用程序,以演示 AMQP Remoting 的工作原理。 就像 Spring Boot 經常一樣,我們只需要選擇並導入正確的 starter 依賴項,就像這裏解釋的:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>我們明確排除使用了 spring-boot-starter-tomcat,因為我們不需要任何嵌入式 HTTP 服務器——如果允許 Maven 導入classpath中的所有傳遞依賴,則會默認啓動它。
4. 服務器應用程序
4.1. 公開服務
如前文所述,我們將暴露一個 <em >CabBookingService</em>,它模擬了一個典型的遠程服務。
首先,聲明一個實現服務接口的 Bean。 這是實際執行服務器端服務調用的 Bean:
@Bean
CabBookingService bookingService() {
return new CabBookingServiceImpl();
}接下來,我們定義服務器將從中檢索調用的隊列。在這種情況下,只需為其指定一個名稱,並在構造函數中提供它即可:
@Bean
Queue queue() {
return new Queue("remotingQueue");
}正如我們之前文章已經知道的,Spring Remoting 的主要概念之一是 Service Exporter,該組件實際上收集來自某些源的調用請求——在本例中是RabbitMQ隊列——並調用服務實現的所需方法。
在本例中,我們定義了一個AmqpInvokerServiceExporter,正如您所看到的,它需要對AmqpTemplate的引用。 AmqpTemplate類由Spring Framework提供,並以同樣的方式簡化了處理AMQP兼容的消息系統,就像JdbcTemplate使處理數據庫變得更容易。
由於它將被Spring Boot的自動配置模塊自動提供,因此我們不會顯式定義該AmqpTemplate Bean。
@Bean AmqpInvokerServiceExporter exporter(
CabBookingService implementation, AmqpTemplate template) {
AmqpInvokerServiceExporter exporter = new AmqpInvokerServiceExporter();
exporter.setServiceInterface(CabBookingService.class);
exporter.setService(implementation);
exporter.setAmqpTemplate(template);
return exporter;
}最後,我們需要定義一個負責從隊列中消費消息並轉發到指定監聽器 的 容器</em/>。
然後,我們將這個 容器</em/>連接到我們在上一步驟中創建的 服務出口</em/>,以便接收隊列中的消息。 此時,ConnectionFactory</em/> 同樣由 Spring Boot</em/> 提供,就像 AmqpTemplate</em/> 一樣。
@Bean
SimpleMessageListenerContainer listener(
ConnectionFactory facotry,
AmqpInvokerServiceExporter exporter,
Queue queue) {
SimpleMessageListenerContainer container
= new SimpleMessageListenerContainer(facotry);
container.setMessageListener(exporter);
container.setQueueNames(queue.getName());
return container;
}4.2. 配置
請務必設置 application.properties 文件,以便 Spring Boot 可以配置基本對象。參數的值也會取決於 RabbitMQ 的安裝方式。
例如,當 RabbitMQ 在與此示例運行相同的機器上運行時,以下配置可能是一個合理的配置:
spring.rabbitmq.dynamic=true
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.host=localhost5. 客户端應用程序
5.1. 調用遠程服務
現在讓我們處理客户端。 同樣,我們需要定義調用消息將寫入的隊列。 我們需要確保客户端和服務器使用相同的名稱。
@Bean
Queue queue() {
return new Queue("remotingQueue");
}在客户端,我們需要比服務端更復雜的配置。事實上,我們需要定義與相關 Binding 相關的 Exchange:
@Bean
Exchange directExchange(Queue someQueue) {
DirectExchange exchange = new DirectExchange("remoting.exchange");
BindingBuilder
.bind(someQueue)
.to(exchange)
.with("remoting.binding");
return exchange;
}關於 RabbitMQ 的主要概念,即 Exchange(交換器)和 Binding(綁定)的介紹,可參考 這裏。
由於 Spring Boot 不會自動配置 AmqpTemplate,因此我們需要手動設置它,並指定一個 routing key。 在進行此操作時,我們需要仔細核實 routing key 和 exchange 是否與之前步驟中定義的 Exchange 匹配。
@Bean RabbitTemplate amqpTemplate(ConnectionFactory factory) {
RabbitTemplate template = new RabbitTemplate(factory);
template.setRoutingKey("remoting.binding");
template.setExchange("remoting.exchange");
return template;
}然後,正如我們為其他 Spring Remoting 實現所做的那樣,我們 定義一個 FactoryBean ,它將產生遠程暴露的服務本地代理。這裏沒有什麼特別的,我們只需要提供遠程服務的接口:
@Bean AmqpProxyFactoryBean amqpFactoryBean(AmqpTemplate amqpTemplate) {
AmqpProxyFactoryBean factoryBean = new AmqpProxyFactoryBean();
factoryBean.setServiceInterface(CabBookingService.class);
factoryBean.setAmqpTemplate(amqpTemplate);
return factoryBean;
}現在我們可以像已聲明為本地 Bean 一樣使用遠程服務:
CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));5.2. 部署
對於客户端應用程序,必須正確選擇 <em >application.properties</em > 文件中的值。 在常見的配置中,這些值將與服務器端使用的值完全一致。
5.3. 運行示例
這段內容應該足以演示通過 RabbitMQ 進行遠程調用。接下來,啓動 RabbitMQ 服務器應用程序和調用遠程服務的客户端應用程序。
發生在其背後的過程是,AmqpProxyFactoryBean 將構建一個實現 CabBookingService 的代理。
當在該代理上調用一個方法時,它會在 RabbitMQ 中排隊一個消息,其中包含調用的所有參數以及用於返回結果的隊列名稱。
該消息由 AmqpInvokerServiceExporter 消費,它調用實際的實現。然後,它將結果收集到一個消息中,並將其放置在接收消息中指定的隊列名稱上。
AmqpProxyFactoryBean 接收到結果,並最終返回服務器端最初產生的該值。
6. 結論
在本文中,我們瞭解到如何使用 Spring Remoting 在消息系統之上提供 RPC。
對於主要場景,利用 RabbitMQ 的異步特性可能更合適,但對於一些特定的、有限的場景,同步調用可能更容易理解、更快、更簡單地開發。