1. 概述
在系列前文中,我们探讨了如何利用 Spring Remoting 及相关技术,在 HTTP 通道上实现服务器与客户端间的同步远程过程调用(RPC)。本文将深入 Spring Remoting 在 AMQP 上的实现——这让我们能在异步消息媒介上执行同步 RPC,实现技术上的巧妙融合。
2. 安装 RabbitMQ
市面上有多种兼容 AMQP 的消息系统,我们选择 RabbitMQ 的理由很简单:
- ✅ 成熟稳定的生产级平台
- ✅ 被 Spring 完全支持(两者同属 Pivotal 公司)
若对 AMQP 或 RabbitMQ 不熟悉,可先阅读我们的快速入门指南。安装步骤:
- 访问官方安装指南
- 根据操作系统选择安装方式(Docker/二进制包/包管理器)
- 启动服务(默认端口 5672)
⚠️ 确保服务运行后再进行后续操作
3. Maven 依赖
我们将搭建服务端和客户端 Spring Boot 应用演示 AMQP Remoting。依赖配置很简单,只需引入正确的 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 服务器 - ✅ Spring Boot 自动配置会处理其他 AMQP 相关组件
4. 服务端应用
4.1. 暴露服务
与之前文章类似,我们暴露一个模拟租车服务的 CabBookingService
。核心步骤:
1. 定义服务实现 Bean
@Bean
CabBookingService bookingService() {
return new CabBookingServiceImpl();
}
2. 创建消息队列
@Bean
Queue queue() {
return new Queue("remotingQueue");
}
3. 配置服务导出器
Spring Remoting 的核心是 Service Exporter,这里使用 AmqpInvokerServiceExporter
:
@Bean AmqpInvokerServiceExporter exporter(
CabBookingService implementation, AmqpTemplate template) {
AmqpInvokerServiceExporter exporter = new AmqpInvokerServiceExporter();
exporter.setServiceInterface(CabBookingService.class);
exporter.setService(implementation);
exporter.setAmqpTemplate(template);
return exporter;
}
AmqpTemplate
由 Spring Boot 自动配置提供- 导出器负责从队列接收调用并执行服务方法
4. 配置消息监听容器
连接队列与导出器的关键组件:
@Bean
SimpleMessageListenerContainer listener(
ConnectionFactory facotry,
AmqpInvokerServiceExporter exporter,
Queue queue) {
SimpleMessageListenerContainer container
= new SimpleMessageListenerContainer(facotry);
container.setMessageListener(exporter);
container.setQueueNames(queue.getName());
return container;
}
ConnectionFactory
同样由 Spring Boot 自动配置- 容器持续消费队列消息并转发给导出器
4.2. 配置文件
在 application.properties
中配置 RabbitMQ 连接参数(示例为本地默认配置):
spring.rabbitmq.dynamic=true
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.host=localhost
⚠️ 生产环境请修改默认凭据!
5. 客户端应用
5.1. 调用远程服务
客户端配置稍复杂,需要额外定义交换器和绑定:
1. 定义队列(与服务端同名)
@Bean
Queue queue() {
return new Queue("remotingQueue");
}
2. 配置交换器与绑定
@Bean
Exchange directExchange(Queue someQueue) {
DirectExchange exchange = new DirectExchange("remoting.exchange");
BindingBuilder
.bind(someQueue)
.to(exchange)
.with("remoting.binding");
return exchange;
}
- 交换器(Exchange)和绑定(Binding)是 RabbitMQ 核心概念
- 官方教程 有详细解释
3. 配置 AmqpTemplate
@Bean RabbitTemplate amqpTemplate(ConnectionFactory factory) {
RabbitTemplate template = new RabbitTemplate(factory);
template.setRoutingKey("remoting.binding");
template.setExchange("remoting.exchange");
return template;
}
- 路由键和交换器名称需与服务端绑定匹配
4. 创建服务代理
@Bean AmqpProxyFactoryBean amqpFactoryBean(AmqpTemplate amqpTemplate) {
AmqpProxyFactoryBean factoryBean = new AmqpProxyFactoryBean();
factoryBean.setServiceInterface(CabBookingService.class);
factoryBean.setAmqpTemplate(amqpTemplate);
return factoryBean;
}
5. 使用远程服务(像本地 Bean 一样)
CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));
5.2. 配置文件
客户端 application.properties
通常与服务端配置一致:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
5.3. 运行示例
执行流程解析:
AmqpProxyFactoryBean
创建CabBookingService
代理- 调用代理方法时:
- 将调用参数和返回队列名封装成消息
- 发送到 RabbitMQ 队列
- 服务端
AmqpInvokerServiceExporter
:- 接收消息并调用实际服务
- 将结果放入指定的返回队列
- 客户端代理接收结果并返回
💡 这种同步调用模型在异步消息系统上实现了 RPC 语义,但本质上还是基于消息传递。
6. 结论
本文展示了如何通过 Spring Remoting 在消息系统上实现 RPC。虽然这种用法不是主流(RabbitMQ 的异步特性通常更受青睐),但在特定场景下:
- ✅ 需要同步调用语义
- ✅ 希望利用现有消息基础设施
- ✅ 团队熟悉 Spring Remoting 模型
这种方案能提供更直观的开发体验。完整源码可在 GitHub 获取。