使用 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 ...
随机推荐
- wpf 导出Excel
private void Button_Click_1(object sender, RoutedEventArgs e) { ExportDataGridSaveAs(true, this.data ...
- [UWP开发] 在低版本中使用亚克力刷以及部分高版本控件
写于2018.10.3 仅在我的PC(17763)和WP(Limia 950 XL 15254)上测试过 微软在build 16299中引入了亚克力刷,但是Win10m并没有活那么久,lumia手机在 ...
- Python文件读写及网站显示
一.关于文件读写的笔记 (一) 文件概述 文件是一个存储在辅助存储器上的数据序列,可以包含任何数据内容 文件都是按照2进制进行存储的,但在表现形式上有2种:文本文件和二进制文件. 1. 文本文件 文本 ...
- 阿里云ECS 介绍
1.阿里云产品概述 1 2.阿里云基础架构介绍 2 3. ECS产品概念和功能 6 4. ECS运维管理和API 12 1.阿里云产品概述 2.阿里云基础架构介绍 ECS 主要有五个主要的组成部分 作 ...
- ROC,AUC,Precision,Recall,F1的介绍与计算
1. 基本概念 1.1 ROC与AUC ROC曲线和AUC常被用来评价一个二值分类器(binary classifier)的优劣,ROC曲线称为受试者工作特征曲线 (receiver operatin ...
- Linux执行.sh文件,提示No such file or directory的问题的解决方法
亲测有效:http://www.jb51.net/LINUXjishu/56395.html Linux执行.sh文件,提示No such file or directory的问题的解决方法 在win ...
- Kafka连接SparkStreaming的两种方式
第一种方式代码: import org.apache.spark.storage.StorageLevel import org.apache.spark.{HashPartitioner, Spar ...
- 使用代码查看Nutch爬取的网站后生成的SequenceFile信息
必须针对data文件中的value类型来使用对应的类来查看(把这个data文件,放到了本地Windows的D盘根目录下). 代码: package cn.summerchill.nutch; impo ...
- 【Canal源码分析】client工作过程
client的工作过程,需要我们自己去编写对应的逻辑,我们目前只能从example写的例子来看.目前examle中提供了两个例子,一个是单机的,一个是集群的cluster,我们后续如果需要进行开发的话 ...
- 深入理解SpringCloud之Eureka注册过程分析
eureka是一种去中心化的服务治理应用,其显著特点是既可以作为服务端又可以作为服务向自己配置的地址进行注册.那么这篇文章就来探讨一下eureka的注册流程. 一.Eureka的服务端 eureka的 ...