使用 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 官网地址如下
启动 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 个功能菜单
- Home: 基本信息
- Queues: 管理的队列
- Topics: 查看所管理的主题
- Subscribers: 查看相关主题的订阅者
- Connections: 查看客户端的连接信息
- Network: 查看网络相关信息
- Scheduled: 查看 ActiveMQ 内部运行的定时任务
- 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 用于删除当前队列
参考
- 《架构探险—轻量级微服务架构》
使用 ActiveMQ 实现JMS 异步调用的更多相关文章
- 消息队列-推/拉模式学习 & ActiveMQ及JMS学习
一种分类是推和拉 . 还有一种分类是 Queue 和 Pub/Sub . 先看的这一篇:http://blog.csdn.net/heyutao007/article/details/50131089 ...
- ActiveMQ基本详解与总结& 消息队列-推/拉模式学习 & ActiveMQ及JMS学习
转自:https://www.cnblogs.com/Survivalist/p/8094069.html ActiveMQ基本详解与总结 基本使用可以参考https://www.cnblogs.co ...
- ActiveMQ入门系列之应用:Springboot+ActiveMQ+JavaMail实现异步邮件发送
现在邮件发送功能已经是几乎每个系统或网址必备的功能了,从用户注册的确认到找回密码再到消息提醒,这些功能普遍的会用到邮件发送功能.我们都买过火车票,买完后会有邮件提醒,有时候邮件并不是买完票立马就能收到 ...
- 006-优化web请求二-应用缓存、异步调用【Future、ListenableFuture、CompletableFuture】、ETag、WebSocket【SockJS、Stomp】
四.应用缓存 使用spring应用缓存.使用方式:使用@EnableCache注解激活Spring的缓存功能,需要创建一个CacheManager来处理缓存.如使用一个内存缓存示例 package c ...
- ActiveMQ:JMS开源框架入门介绍
介绍基本的JMS概念与开源的JMS框架ActiveMQ应用,内容涵盖一下几点: 基本的JMS概念 JMS的消息模式 介绍ActiveMQ 一个基于ActiveMQ的JMS例子程序 一:JMS基本概念 ...
- C#委托异步调用
参考页面: http://www.yuanjiaocheng.net/webapi/mvc-consume-webapi-get.html http://www.yuanjiaocheng.net/w ...
- Direct3D Draw函数 异步调用原理解析
概述 在D3D10中,一个基本的渲染流程可分为以下步骤: 清理帧缓存: 执行若干次的绘制: 通过Device API创建所需Buffer: 通过Map/Unmap填充数据到Buffer中: 将Buff ...
- 一个简单的webservice的demo(下)winform异步调用webservice
绕了一大圈,又开始接触winform的项目来了,虽然很小吧.写一个winform的异步调用webservice的demo,还是简单的. 一个简单的Webservice的demo,简单模拟服务 一个简单 ...
- 浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法(转载)
在调用一个jquery的ajax方法时我们有时会需要该方法返回一个值或者给某个全局变量赋值,可是我们发现程序执行完后并没有获取到我们想要的值,这时很有可能是因为你用的是ajax的异步调用async:t ...
随机推荐
- 在EF中使用Expression自动生成p=>new Entity(){X="",Y="",..}格式的Lambda表达式灵活实现按需更新
一.基本介绍 回忆:最早接触Expression是在学校接触到EF的时候,发现where方法里的参数是Expression<Func<T,bool>>这么一个类型,当 ...
- MariaDB 插入&更新&删除数据(8)
MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,MySQL由于现在闭源了,而能轻松成为MySQ ...
- 「PKUSC2018」真实排名(组合)
一道不错的组合数问题! 分两类讨论: 1.\(a_i\) 没有翻倍,那些 \(\geq a_i\) 和 \(a_j\times 2<a_i\) 的数就没有影响了.设 \(kth\) 为 \(a_ ...
- PHP eval函数
代码: eval("echo'hello world';"); 上边代码等同于下边的代码: echo"hello world"; 在浏览器中都输出:hello ...
- ROC,AUC,Precision,Recall,F1的介绍与计算
1. 基本概念 1.1 ROC与AUC ROC曲线和AUC常被用来评价一个二值分类器(binary classifier)的优劣,ROC曲线称为受试者工作特征曲线 (receiver operatin ...
- Spring Boot日志管理
SpringBoot内部使用Commons Logging来记录日志,但是默认也提供了对常用日志组件的支持,如:Log4j,Logback等.每种Logger都可以通过配置使用控制台或者文件输出日志内 ...
- 人工智能-机器学习之Selenium(chrome驱动,火狐驱动)
selenium是一个用于web应用程序测试的工具,Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE.Mozilla Firefox.Mozilla Suite等 ...
- Django 模板相关
Django 模板相关 视图函数只是直接返回文本,而在实际生产环境中其实很少这样用,因为实际的页面大多是带有样式的HTML代码,这可以让浏览器渲染出非常漂亮的页面.目前市面上有非常多的模板系统,其中最 ...
- KMP算法的next函数求解和分析过程
转自 wang0606120221:http://blog.csdn.net/wang0606120221/article/details/7402688 假设KMP算法中的模式串为P,主串为S,那么 ...
- 整理几个 RPC 框架
gRPChttp://www.grpc.io/https://github.com/grpcMotan支撑微博千亿调用的轻量级RPC框架:Motanhttp://h2ex.com/820WangleW ...