1. Overview
1.概述
We saw in a previous article how Spring Remoting could be used to provide RPC on top of an asynchronous channel as an AMQP queue. However, we can obtain the same result using JMS too.
我们在上一篇文章中看到,Spring Remoting可用于在异步通道之上提供RPC作为AMQP队列。然而,我们也可以使用JMS获得同样的结果。
In this article, we’ll, in fact, explore how to set up remote invocation using Spring Remoting JMS and Apache ActiveMQ as a messaging middleware.
在本文中,我们将探讨如何使用Spring Remoting JMS和Apache ActiveMQ作为消息传递中间件来设置远程调用。
2. Starting an Apache ActiveMQ Broker
2.启动一个Apache ActiveMQ代理
Apache ActiveMQ is an open source message broker that enables applications to exchange information asynchronously, and it is entirely compatible with the Java Message Service API.
Apache ActiveMQ是一个开源消息代理,使应用程序能够异步地交换信息,它与Java消息服务 API完全兼容。
To run our experiment, we firstly need to set up a running instance of ActiveMQ. We can choose among several ways: following the steps described in the official guide, embedding it in a Java application or more simply spinning up a Docker container with the following command:
为了运行我们的实验,我们首先需要建立一个运行中的ActiveMQ实例。我们可以选择几种方式:按照官方指南中描述的步骤,将其嵌入Java应用程序中,或者更简单地使用以下命令启动Docker容器。
docker run -p 61616:61616 -p 8161:8161 rmohr/activemq:5.14.3
This will start an ActiveMQ container that exposes on port 8081 a simple administration web GUI, through which we can check the available queues, connected clients, and other administrative information. JMS clients will need to use port 61616 to connect to the broker and exchange messages instead.
这将启动一个ActiveMQ容器,它在8081端口暴露了一个简单的管理Web GUI,通过它我们可以检查可用的队列、连接的客户端和其他管理信息。JMS客户端将需要使用61616端口来连接到代理并交换消息。
3. Maven Dependencies
3.Maven的依赖性
As in the previous articles covering Spring Remoting, we are going to set up a server and a client Spring Boot applications to show how JMS Remoting works.
如同之前涉及Spring Remoting的文章一样,我们将设置一个服务器和一个客户端Spring Boot应用程序,以展示JMS Remoting如何工作。
As usually we carefully choose the Spring Boot starter dependencies, as explained here:
通常我们会仔细选择Spring Boot启动器的依赖性,如这里解释的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
We explicitly excluded the spring-boot-starter-tomcat in order not to have the Tomcat related .jar files in the classpath.
我们明确排除了spring-boot-starter-tomcat,以便在classpath中没有Tomcat 相关.jar文件。
This, in turn, will prevent the Spring Boot‘s autoconfiguration mechanism to launch an embedded web server when the application starts up since we don’t need it.
这又将阻止Spring Boot的自动配置机制在应用程序启动时启动嵌入式Web服务器,因为我们不需要它。
4. Server Application
4.服务器应用
4.1. Expose the Service
4.1.暴露服务
We’ll set up a server application that exposes the CabBookingService that clients will be able to invoke.
我们将建立一个服务器应用程序,公开客户能够调用的CabBookingService。
The first step is to declare a bean that implements the interface of the service we want to expose to the clients. This is the bean that will execute the business logic on the server:
第一步是声明一个Bean,该Bean实现了我们要向客户公开的服务的接口。这是一个将在服务器上执行业务逻辑的Bean。
@Bean
CabBookingService bookingService() {
return new CabBookingServiceImpl();
}
Let’s then define the queue from which the server will retrieve invocations, specifying its name in the constructor:
然后让我们定义一个队列,服务器将从该队列中检索调用,在构造函数中指定其名称。
@Bean
Queue queue() {
return new ActiveMQQueue("remotingQueue");
}
As we already know from the previous articles, one of the main concepts of Spring Remoting is the Service Exporter, the component that collects the invocation requests from some source, in this case, an ApacheMQ queue ─ and invokes the desired method on the service implementation.
正如我们在之前的文章中所知道的,Spring Remoting的主要概念之一是Service Exporter,这个组件从某个来源收集调用请求,在这里是一个ApacheMQ队列–并在服务实现中调用所需的方法。
To work with JMS, we define a JmsInvokerServiceExporter:
为了与JMS一起工作,我们定义一个JmsInvokerServiceExporter。
@Bean
JmsInvokerServiceExporter exporter(CabBookingService implementation) {
JmsInvokerServiceExporter exporter = new JmsInvokerServiceExporter();
exporter.setServiceInterface(CabBookingService.class);
exporter.setService(implementation);
return exporter;
}
Finally, we need to define a listener that has the responsibility to consume messages. The listener acts as a bridge between ApacheMQ and the JmsInvokerServiceExporter, it listens to invocation messages available on the queue, forwards the invocation to the service exporter and serializes back the results:
最后,我们需要定义一个负责消费消息的监听器。监听器作为ApacheMQ和JmsInvokerServiceExporter之间的桥梁,它监听队列上可用的调用消息,将调用转发给服务出口商并将结果序列化。
@Bean SimpleMessageListenerContainer listener(
ConnectionFactory factory,
JmsInvokerServiceExporter exporter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(factory);
container.setDestinationName("remotingQueue");
container.setConcurrentConsumers(1);
container.setMessageListener(exporter);
return container;
}
4.2. Configuration
4.2.配置
Let’s remember to set up the application.properties file to allow Spring Boot to configure some basic objects, as for instance the ConnectionFactory needed by the listener. The values of the various parameters mainly depend on the way
让我们记得设置application.properties文件,以便让Spring Boot配置一些基本对象,例如,监听器所需的ConnectionFactory。各种参数的值主要取决于以下方式
The values of the various parameters mainly depend on the way ApacheMQ has been installed, and the following one is a reasonable configuration for our Docker container running on the same machine where we’ll run these examples:
各种参数的值主要取决于ApacheMQ的安装方式,以下是我们在运行这些例子的同一台机器上运行的Docker容器的一个合理配置。
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.packages.trusted=org.springframework.remoting.support,java.lang,com.baeldung.api
The spring.activemq.broker-url parameter is a reference to the AMQ port. A deeper explanation is needed for spring.activemq.packages.trusted parameter instead. Since version 5.12.2 ActiveMQ refuses by default any message of type
spring.activemq.broker-url参数是对AMQ端口的引用。需要对spring.activemq.packages.trusted参数进行更深入的解释,而不是。从5.12.2版本开始,ActiveMQ默认拒绝任何类型为
Starting with version 5.12.2 ActiveMQ refuses by default any message of type ObjectMessage, used to exchange serialized Java object because it is considered a potential vector for a security attack in some contexts.
从5.12.2版本开始,ActiveMQ默认拒绝任何ObjectMessage类型的消息,用于交换序列化的Java对象,因为它在某些情况下被认为是安全攻击的潜在载体。
Anyhow, it is possible to instruct AMQ to accept serialized objects in specified packages. org.springframework.remoting.support is the package that contains the main messages that represent the invocation of a remote method and its result. The package
无论如何,我们可以指示AMQ接受指定包中的序列化对象。org.springframework.remoting.support是包含表示调用远程方法及其结果的主要消息的包。该包
The package com.baeldung.api contains the parameters and the results of our service. java.lang is added because the object that represents the result of the cab booking references a String, so we need to serialize that too.
包com.baeldung.api包含我们服务的参数和结果。java.lang被添加,因为代表出租车预订结果的对象引用了String,所以我们也需要将其序列化。
5. Client Application
5.客户端应用
5.1. Invoke the Remote Service
5.1.调用远程服务
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.
现在我们来解决客户端的问题。同样,我们需要定义调用信息将被写入的队列。我们需要仔细检查客户端和服务器是否使用相同的名字。
@Bean
Queue queue() {
return new ActiveMQQueue("remotingQueue");
}
We then need to set up an exporter:
然后我们需要设置一个出口商。
@Bean
FactoryBean invoker(ConnectionFactory factory, Queue queue) {
JmsInvokerProxyFactoryBean factoryBean = new JmsInvokerProxyFactoryBean();
factoryBean.setConnectionFactory(factory);
factoryBean.setServiceInterface(CabBookingService.class);
factoryBean.setQueue(queue);
return factoryBean;
}
We can now use the remote service as if it was declared as a local bean:
我们现在可以使用远程服务,就像它被声明为一个本地Bean一样。
CabBookingService service = context.getBean(CabBookingService.class);
out.println(service.bookRide("13 Seagate Blvd, Key Largo, FL 33037"));
5.2. Run the Example
5.2.运行实例
Also for the client application, we have to choose the values in the application properly.properties file. In a common setup, those would exactly match the ones used on the server side.
另外,对于客户端应用程序,我们必须在应用程序中正确选择值.properties文件。在一个常见的设置中,这些值将与服务器端使用的值完全一致。
This should be enough to demonstrate the remote invocation through Apache AMQ. So, let’s first start ApacheMQ, then the server application, and finally the client application that will invoke the remote service.
这应该足以证明通过Apache AMQ进行的远程调用。因此,让我们首先启动ApacheMQ,然后是服务器应用程序,最后是将调用远程服务的客户端应用程序。
6. Conclusion
6.结论
In this quick tutorial, we saw how we could use Spring Remoting to provide RPC on top of a JMS system as AMQ.
在这个快速教程中,我们看到了如何使用Spring Remoting在JMS系统之上提供RPC作为AMQ。
Spring Remoting keeps on demonstrating how it is easy to set up asynchronous call regardless of the underlying channel quickly.
Spring Remoting不断地展示了如何快速地设置异步调用而不考虑底层通道。
As usual, you’ll find the sources over on GitHub.
像往常一样,你可以在GitHub上找到源代码。