使用 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 ...
随机推荐
- Flask系列07--Flask中的CBV, 蓝图的CBV
一.CBV使用 class base view 和django中类似 class Login(views.MethodView): # methods=["POST"," ...
- jzoj4235 序列
取前50個數暴力即可 #include<bits/stdc++.h> using namespace std; int n,m,a[100010],q[5]; int main(){ sc ...
- TmsTimeUtils 时间戳
package com.sprucetec.tms.utils; import java.math.BigDecimal;import java.text.DateFormat;import java ...
- GC日志时间分析
在GC日志里,一条完整的GC日志记录最后,会带有本次GC所花费的时间,如下面这一条新生代GC: [GC [DefNew: 3298K->149K(5504K), secs] [Times: us ...
- js 标准二维数组变一维数组的方法
问题:[[0, 1], [2, 3], [4, 5]] -> [0, 1, 2, 3, 4, 5]? 方法一 利用es5的arr.reduce(callback[, initialValue]) ...
- SpringMVC初探-HelloWorld
MVC的概念 MVC是一种设计模式,即Model--View-Controller,模型--视图--控制器 Model(模型)表示应用程序核心(比如数据库记录列表). View(视图)显示数据(数据库 ...
- Python语法基础练习
- odoo开发笔记 -- 前端开发相关
https://www.cnblogs.com/lyzg/p/5634565.html http://dmyz.org/archives/598 https://www.jianshu.com/p/6 ...
- JDK中线程组ThreadGroup
如果线程有100条...分散的不好管理... 线程同样可以分组ThreadGroup类. 线程组表示一个线程的集合.此外,线程组也可以包含其他线程组.线程组构成一棵树,在树中,除了初始线程组外,每个线 ...
- N元马尔科夫链的实现
马尔可夫模型(Markov Model)是一种统计模型,广泛应用在语音识别,词性自动标注,音字转换,概率文法等各个自然语言处理等应用领域.经过长期发展,尤其是在语音识别中的成功应用,使它成为一种通用的 ...