Spring Remoting with AMQP – 使用AMQP的Spring Remoting

最后修改: 2017年 4月 22日

1. Overview


We saw in the previous installments of the series how we can leverage Spring Remoting and the related technologies to enable synchronous Remote Procedure Calls on top of an HTTP channel between a server and a client.

我们在该系列的前几期中看到,我们可以利用Spring Remoting和相关技术,在服务器和客户端之间的HTTP通道之上实现同步的远程过程调用

In this article, we’ll explore Spring Remoting on top of AMQP, which makes it possible to execute synchronous RPC while leveraging a medium that is inherently asynchronous.

在这篇文章中,我们将探讨Spring RemotingAMQP之上的情况,这使得执行同步RPC同时利用固有的异步媒介成为可能。

2. Installing RabbitMQ


There are various messaging systems that are compatible with AMQP that we could use, and we choose RabbitMQ because it’s a proven platform and it’s fully supported in Spring – both products are managed by the same company (Pivotal).


If you’re not acquainted with AMQP or RabbitMQ you can read our quick introduction.


So, the first step is to install and start RabbitMQ. There are various ways to install it – just choose your preferred method following the steps mentioned in the official guide.

因此,第一步是安装并启动RabbitMQ。有多种方法来安装它 – 只要按照官方指南中提到的步骤选择你喜欢的方法即可

3. Maven Dependencies


We are going to set up server and client Spring Boot applications to show how AMQP Remoting works. As is often the case with Spring Boot, we just have to choose and import the correct starter dependencies, as explained here:

我们将设置服务器和客户端Spring Boot应用程序,以展示AMQP Remoting如何工作。与Spring Boot的情况一样,我们只需选择并导入正确的启动器依赖项,如这里所解释的


We explicitly excluded the spring-boot-starter-tomcat because we don’t need any embedded HTTP server – that would be automatically started instead if we allowed Maven to import all the transitive dependencies in the classpath.


4. Server Application


4.1. Expose the Service


As we showed in the previous articles, we’ll expose a CabBookingService that simulates a likely remote service.


Let’s start by declaring a bean that implements the interface of the service we want to make remotely callable. This is the bean that will actually execute the service call at server-side:


CabBookingService bookingService() {
    return new CabBookingServiceImpl();

Let’s then define the queue from which the server will retrieve invocations. It’s enough, in this case, to specify a name for it, providing it in the constructor:


Queue queue() {
    return new Queue("remotingQueue");

As we already know from the previous articles, one of the main concepts of Spring Remoting is the Service Exporter, the component that actually collects the invocation requests from some source ─ in this case, a RabbitMQ queue ─ and invokes the desired method on the service implementation.

正如我们在之前的文章中所知,Spring Remoting的主要概念之一是Service Exporter,这个组件实际上是从某个来源(在这里是RabbitMQ队列)收集调用请求,并在服务实现上调用所需方法。

In this case, we define an AmqpInvokerServiceExporter that ─ as you can see ─ needs a reference to an AmqpTemplate. The AmqpTemplate class is provided by the Spring Framework and eases the handling of AMQP-compatible messaging systems the same way the JdbcTemplate makes easier to deal with databases.

在这种情况下,我们定义了一个AmqpInvokerServiceExporter,正如你所看到的,它需要一个对AmqpTemplate的引用。AmqpTemplate类由Spring Framework提供,它简化了对AMQP-兼容的消息系统的处理,就像JdbcTemplate简化了对数据库的处理。

We won’t explicitly define such AmqpTemplate bean because it will be automatically provided by Spring Boot‘s auto-configuration module:

我们不会明确定义这种AmqpTemplate豆,因为它将由Spring Boot的自动配置模块自动提供。

@Bean AmqpInvokerServiceExporter exporter(
  CabBookingService implementation, AmqpTemplate template) {
    AmqpInvokerServiceExporter exporter = new AmqpInvokerServiceExporter();
    return exporter;

Finally, we need to define a container that has the responsibility to consume messages from the queue and forward them to some specified listener.


We’ll then connect this container to the service exporter, we created in the previous step, to allow it to receive the queued messages. Here the ConnectionFactory is automatically provided by Spring Boot the same way the AmqpTemplate is:

然后,我们将将这个容器连接到我们在上一步创建的服务输出器,以使其能够接收排队的消息。这里的ConnectionFactory是由Spring Boot自动提供的,与AmqpTemplate的方式相同。

SimpleMessageListenerContainer listener(
  ConnectionFactory facotry, 
  AmqpInvokerServiceExporter exporter, 
  Queue queue) {
    SimpleMessageListenerContainer container
     = new SimpleMessageListenerContainer(facotry);
    return container;

4.2. Configuration


Let’s remember to set up the application.properties file to allow Spring Boot to configure the basic objects. Obviously, the values of the parameters will also depend on the way RabbitMQ has been installed.

让我们记得设置application.properties文件,以便让Spring Boot配置基本对象。显然,参数的值也将取决于RabbitMQ的安装方式。

For instance, the following one could be a reasonable configuration when RabbitMQ runs it the same machine where this example runs:



5. Client Application


5.1. Invoke the Remote Service


Let’s tackle the client now. Again, we need to define the queue where invocation messages will be written to. We need to double-check that both client and server use the same name.


Queue queue() {
    return new Queue("remotingQueue");

At client-side, we need a slightly more complex setup than on the server side. In fact, we need to define an Exchange with the related Binding:


Exchange directExchange(Queue someQueue) {
    DirectExchange exchange = new DirectExchange("remoting.exchange");
    return exchange;

A good intro on the main concepts of RabbitMQ as Exchanges and Bindings is available here.


Since Spring Boot does not auto-configure the AmqpTemplate, we must set one up ourselves, specifying a routing key. In doing so, we need to double-check that the routing key and the exchange match with the one used to define the Exchange in the previous step:

由于Spring Boot不会自动配置AmqpTemplate,我们必须自己设置一个,指定一个routing key。在这样做的时候,我们需要仔细检查routing keyexchange是否与上一步中用来定义Exchange的匹配。

@Bean RabbitTemplate amqpTemplate(ConnectionFactory factory) {
    RabbitTemplate template = new RabbitTemplate(factory);
    return template;

Then, as we did with other Spring Remoting implementations, we define a FactoryBean that will produce local proxies of the service that is remotely exposed. Nothing too fancy here, we just need to provide the interface of the remote service:

然后,正如我们对其他Spring Remoting实现所做的那样,我们定义一个FactoryBean,它将产生远程暴露的服务的本地代理。这里没有太多花哨的东西,我们只需要提供远程服务的接口。

@Bean AmqpProxyFactoryBean amqpFactoryBean(AmqpTemplate amqpTemplate) {
    AmqpProxyFactoryBean factoryBean = new AmqpProxyFactoryBean();
    return factoryBean;

We can now use the remote service as if it was declared as a local bean:


CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));

5.2. Setup


Also for the client application, we have to properly choose the values in the application.properties file. In a common setup, those would exactly match the ones used on the server side.


5.3. Run the Example


This should be enough to demonstrate the remote invocation through RabbitMQ. Let’s then start RabbitMQ, the server application, and the client application that invokes the remote service.

这应该足以证明通过 RabbitMQ 进行的远程调用。然后让我们启动 RabbitMQ、服务器应用程序和调用远程服务的客户端应用程序。

What happens behind the scenes is that the AmqpProxyFactoryBean will build a proxy that implements the CabBookingService.


When a method is invoked on that proxy, it queues a message on the RabbitMQ, specifying in it all the parameters of the invocation and a name of a queue to be used to send back the result.


The message is consumed from the AmqpInvokerServiceExporter that invokes the actual implementation. It then collects the result in a message and places it on the queue which name was specified in the incoming message.


The AmqpProxyFactoryBean receives back the result and, finally, returns the value that has been originally produced at the server side.


6. Conclusion


In this article, we saw how we can use Spring Remoting to provide RPC on top of a messaging system.

在这篇文章中,我们看到了如何使用Spring Remoting来在消息系统之上提供RPC。

It’s probably not the way to go for the main scenarios where we probably prefer to leverage the asynchronicity of RabbitMQ, but in some selected and limited scenarios, a synchronous call can be easier to understand and quicker and simpler to develop.


As usual, you’ll find the sources on over on GitHub.