使用 ActiveMQ 实现JMS 异步调用

简介

服务之间的同步调用,可以使用 HTTP 或 RPC 来完成,但并非所有的调用都需要同步,有些场景下,当客户端调用服务端时,并不需要等待服务端做出响应,此时就应该使用异步调用。异步调用的常用方式是基于 MQ (Message Queue) 来实现的。下文会以 ActiveMQ 为例进行讲解。

ActiveMQ 是 Java 世界中最为流行的开源消息中间件,它不仅功能强大,而且性能稳定。它可全面支持 JMS(Java 消息服务)技术规范,为 Java 应用程序提供标准的 JMS API。

此外 ActiveMQ 具备与 Spring 框架整合的能力,它一直都是 Spring 应用程序的消息中间件标配。同样, Spring Boot 也提供了 ActiveMQ 的开箱即用的插件,只需要几项配置,就能接入 ActiveMQ,并轻松使用 JMS API 编写异步消息通信程序。

Active MQ 官网地址如下

http://activemq.apache.org

启动 ActiveMQ 服务器

先使用 docker 安装 ActiveMQ ,目前 ActiveMQ 官方并未提供相应的 Docker 镜像,我们选择使用第三方镜像 webcenter/activemq

docker pull webcenter/activemq:5.14.3

接下来运行 ActiveMQ

docker run  -d -p 8161:8161 -p 61616:61616 -e ACTIVEMQ_ADMIN_LOGIN=admin -e ACTIVEMQ_ADMIN_PASSWORD=admin --name activemq webcenter/activemq:5.14.3

在启动 ActiveMQ 容器时,容器对宿主机暴露了两个端口号:

  • 8161: 表示 ActiveMQ 控制台端口号,可在浏览器中通过控制台来执行 ActiveMQ 的相关操作
  • 61616: 表示 ActiveMQ 所监听的 TCP 端口号,应用程序可通过该端口号与 ActiveMQ 建立 TCP 连接,并完成后续的异步消息通信

此外,在启动 ActiveMQ 容器时,还提供了两个环境变量

  • ACTIVEMQ_ADMIN_LOGIN: 用于设置控制台管理员的用户名,默认为 admin
  • ACTIVEMQ_ADMIN_PASSWORD: 用于设置控制台管理员的密码,默认为 admin

查看控制台

webcenter/activemq 镜像拥有一个基于 Web 的控制台,可通过浏览器访问。容器启动完毕后,可以打开浏览器,并在地址栏中输入 http://localhost:8161

点击 Manage ActiveMQ borker 链接,浏览器将弹出一个对话框,此时输入用户名和密码,认证通过后会进入管理界面

在管理界面中,包括 8 个功能菜单

  1. Home: 基本信息
  2. Queues: 管理的队列
  3. Topics: 查看所管理的主题
  4. Subscribers: 查看相关主题的订阅者
  5. Connections: 查看客户端的连接信息
  6. Network: 查看网络相关信息
  7. Scheduled: 查看 ActiveMQ 内部运行的定时任务
  8. Send: 通过表单方式查看向队列或主题发送具体消息

ActiveMQ 的消息通道

ActiveMQ 管理了两类消息通道,一类是队列(Queue),另一类叫做主题(Topic)。

Queue

Queue 用于解决消息的 点对点 通信问题,也就是说,消息从生产者(Producer) 发出后,首先进入 ActiveMQ 某个指定的 Queue 中,然后再将消息传送给其中一个消费者(Consumer)。

Topic

Topic 用于解决消息的发布与订阅(Publish-subscribe) 通信问题,也就是说,消息从 Producer 发出后,首先将其发布到 ActiveMQ 某个指定的 Topic 上,然后将此消息分发给每个订阅者(Subscriber) 。

比较

在具体场合下,灵活使用以上两种通信模式来实现 Producer 与 Consumer/Subscriber 间的异步调用,从而解决调用方的耦合问题。可见,Queue 能解决调用缓冲问题,Topic 能解决消息广播问题, Queue 与 Topic 都能解决掉调用耦合问题,这些技术都为一个好的软件架构提供了有效的支撑。

开发生产者和消费者

下面就以 Queue 为例,将 ActiveMQ 与 Spring Boot 进行整合,将 Producer 作为客户端, Consumer 作为服务端,通过 Queue 实现客户端与服务端的异步调用

开发服务端(消费者)

首先创建一个名为 acitvemq-hello-server 的 spring boot 项目,如果在 eclipse 中安装了 Spring Tools ,可以在新建时选择 New Spring Starter Project 选项。或者新建 Maven 工程。对应的 maven 依赖如下

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.19.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-activemq</artifactId>
		</dependency>
	</dependencies>

在 Spring Boot 框架中已经内置了对 ActiveMQ 的支持,我们只需要依赖 spring-boot-starter-mq 就能启动 ActiveMQ,此时还需要在 application.properties 文件中添加 ActiveMQ 配置项

spring.activemq.broker-url=tcp://10.104.10.1:61616
spring.activemq.user=admin
spring.activemq.password=admin

接下来创建 HelloServer 的类,封装服务端相关代码

@Component
public class HelloServer {
    @JmsListener(destination="hello-queue")
    public void receive(String message) {
        System.out.println(message);
    }
}

使用 @Component 注解,说明它可被 Spring IoC 容器所管理。此时只需要使用 @JmsListener 注解,并将其绑定到 receive() 方法上,就能从 ActiveMQ 中接收响应的消息。

@JmsListener 注解中需要添加一个 destination 属性来指定 Queue/Topic 的名称,该名称具有唯一性。消息将以一个 String 类型参数的形式传入方法体中,也可以接收其他类型的消息,这取决于客户端发送的消息是哪种类型。Spring JMS 将消息放入 ActiveMQ 时会进行序列化,当消息从 ActiveMQ 取出时将进行反序列化,应用程序无需关注这些底层细节,只需要将精力放在业务逻辑上。

最后,编写一个 Spring Boot 应用程序启动类来启动服务端(使用 spring tools 工具会自动生成)

@SpringBootApplication
public class ActivemqHelloServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ActivemqHelloServerApplication.class, args);
	}
}

当服务端启动完毕后,将一直监听 ActiveMQ 的 hello-queue 队列中即将到来的消息,消息由客户端来发送。

开发客户端(生产者)

创建一个名为 active-mq-client 的 Maven 项目, pom.xml 文件内容与服务端相似。application.properties 文件与服务端相同。

接下来创建一个名为 HelloClient 的类,将其作为客户端。

@Component
public class HelloClient {
    
    @Autowired
    private JmsTemplate jmsTemplate;
    
    public void send(String message) {
        jmsTemplate.convertAndSend("hello-queue", message);
    }
}

这里使用了 @Autowired 注解, JmsTemplate 对象注入进来,还编写了一个 send() 方法,在该方法中调用 JmsTemplate 对象的 convertAndSend 来转换并发送消息。

最后使用 Spring Boot 应用程序启动类来启动客户端

@SpringBootApplication
public class ActivemqHelloClientApplication {

    @Autowired
    private HelloClient helloClient;
    
    @PostConstruct
    public void init() {
        helloClient.send("hello world");
    }
    
	public static void main(String[] args) {
		SpringApplication.run(ActivemqHelloClientApplication.class, args);
	}

}

需要注意的是, init() 方法带有 @PostConstruct 注解,表示 Spring IoC 容器实例化 ActivemqHelloClientApplication 类后将调用该方法。

运行 main() 方法可以启动客户端应用程序,并可以在服务端应用程序控制台中看到 client 发送的消息,也可以在 ActiveMQ 控制台中查看队列的当前状态

Queue 表格中列明的含义如下

  • Name 表示队列名称,可在应用程序中自动创建,也可在 ActiveMQ 控制台中手动创建
  • Number Of Pending Messages 表示阻塞在队列中未经消费的消息条数
  • Number Of Consumers 表示正在与 ActiveMQ 建立连接的消费者数量
  • Messages Enqueued 表示进入队列的消息数量
  • Messages Dequeued 表示离开队列的消息数量

此外,还有下面几种操作

  • Browser 用于查看当前队列中消息的相关细节
  • Active Consumers 用于查看当前活动消费者的相关信息
  • Active Producers 用于查看当前活动生产者的相关信息
  • Send To 用于向当前队列中发送具体消息
  • Purge 用于清空队列中的消息
  • Delete 用于删除当前队列

参考

  • 《架构探险—轻量级微服务架构》
posted @ 2019-01-09 09:01  ReyCG  阅读(1163)  评论(0编辑  收藏  举报