本文主要对ActiveMQ应用场景及应用实例进行了总结,本文源码已上传github, 链接见文末。
ActiveMQ简介
1.java消息服务:
不同系统之间的信息交换,是我们开发中比较常见的场景,比如系统A要把数据发送给系统B,这个问题我们应该如何去处理? 1999年,原来的SUN公司领衔提出了一种面向消息的中间件服务–JMS规范(标准);常用的几种信息交互技术(httpClient、hessian、dubbo、jms、webservice 五种).
2.JMS概述:
JMS即Java消息服务(Java Message Service的简称),是Java EE 的标准/规范之一。这种规范(标准)指出:消息的发送应该是异步的、非阻塞的。也就是说消息的发送者发送完消息后就直接返回了,不需要等待接收者返回后才能返回,发送者和接收者可以说是互不影响。所以这种规范(标准)能够减轻或消除系统瓶颈,实现系统之间去除耦合,提高系统的整体可伸缩性和灵活性。JMS只是Java EE中定义的一组标准API,它自身并不是一个消息服务系统,它是消息传送服务的一个抽象,也就是说它定义了消息传送的接口而并没有具体实现。
3.ActiveMQ概述:
JMS只是消息服务的一组规范和接口,并没有具体的实现,而ActiveMQ就是JMS规范的具体实现;它是Apache下的一个项目,采用Java语言开发;是一款非常流行的开源消息服务器.
4.应用场景
(1)异步处理
如下图,用户注册后,直接响应,邮件和短信发送通过消息队列异步处理。
对比下图,缩短了响应时间,增加了系统QPS
(2)应用解耦
如下图,应用消息队列后,库存系统不能正常使用,也不直接影响订单系统。
- 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
- 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
- 对比下图:传统模式下,库存系统一断无法正常使用,订单系统也将无法正常使用。
(3)流量消峰
如下图,一个典型应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。这样,用户的请求,服务器接收后,首先写入消息队列。
这样做有如下好处:
- 可以控制活动的人数:假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
- 可以缓解短时间内高流量压垮应用:秒杀业务根据消息队列中的请求信息,再做后续处理
(4)其他场景
其他的场景还有:日志处理,消息通讯等。
ActiveMQ安装配置
1.下载安装
本文介绍Mac OS下安装
(1)推荐方式:通过brew安装ActiveMQ
$ brew install activemq
安装完后,通过下列命令查看版本来确定是否安装成功:
$activemq --version
(2)也可自行下载解压安装:
下载地址:http://activemq.apache.org/components/classic/download/
注意mac下自行解压安装后启动方式是:
// 即所有命令要在 active安装目录/bin/macosx下执行
cd /usr/local/apache-activemq-5.15.9/bin/macosx
./activemq start
2.启动activeMQ服务
activemq start
显示:Running ActiveMQ Broker… 表示启动成功
访问地址:http://localhost:8161/admin/ 默认账号:admin / admin
默认端口:8161 (可自行修改/usr/local/apache-activemq-5.15.9/conf/jetty.xml)
3.ActiveMQ常用命令
Tasks:
browse - Display selected messages in a specified destination.
bstat - Performs a predefined query that displays useful statistics regarding the specified broker
consumer - Receives messages from the broker
create - Creates a runnable broker instance in the specified path.
decrypt - Decrypts given text
dstat - Performs a predefined query that displays useful tabular statistics regarding the specified destination type
encrypt - Encrypts given text
export - Exports a stopped brokers data files to an archive file
list - Lists all available brokers in the specified JMX context
producer - Sends messages to the broker
purge - Delete selected destination's messages that matches the message selector
query - Display selected broker component's attributes and statistics.
start - Creates and starts a broker using a configuration file, or a broker URI.
stop - Stops a running broker specified by the broker name.
Task Options (Options specific to each task):
--extdir <dir> - Add the jar files in the directory to the classpath.
--version - Display the version information.
-h,-?,--help - Display this help information. To display task specific help, use Main [task] -h,-?,--help
ActiveMQ之Hello World 实例
不知从什么时候起,渐渐习惯这种了解基本的理论后直接快速上手一个hello World实例~~ 不过,这确实是现学现用的一个好方法,但是有时间的话,还是觉得要看看书系统地了解一下,这样,遇到了新问题,也有迹可循。
1.启动activemq访问:http://localhost:8161/admin/
点击queues如图:此时消息队列为空
2.新建maven项目
添加mq依赖:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>
最终pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.leon</groupId>
<artifactId>activemq-helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>activemq-helloworld</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<activemq.version>5.15.9</activemq.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-all -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.编写消息生产者与消费者代码
最终目录结构如下:
(1)Const.java :常量类
package com.leon.activemq_helloworld;
/**常量类
*
* @author leon
*
*/
public interface Const {
String MQ_DISTINATION_NAME = "mq_demo_queue";
}
(2)Producer.java:生产者
package com.leon.activemq_helloworld;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 生产者
*
* @author leon
*
*/
public class Producer {
public static void send(String msg, String distinationName) throws Exception {
// 1、创建工厂连接对象,要指定ip和端口号(61616是默认的openwrite端口,在conf/activemq.xml里配置)
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
// 2、使用连接工厂创建一个连接对象
Connection connection = connectionFactory.createConnection();
// 3、开启连接
connection.start();
// 4、使用连接对象创建会话(session)对象
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5、使用会话对象创建目标对象,分为queue(p2p:一对一)和topic(发布订阅:一对多)
Queue queue = session.createQueue(distinationName);
// 6、使用会话对象创建消息生产者对象
MessageProducer producer = session.createProducer(queue);
// 7、使用会话对象创建一个消息对象
TextMessage textMessage = session.createTextMessage(msg);
// 8、发送消息
producer.send(textMessage);
// 9、关闭资源
producer.close();
session.close();
connection.close();
}
public static void main(String[] args) {
try {
send("hello world!", Const.MQ_DISTINATION_NAME);
System.out.println("消息已发送");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(3)Consumer.java:消费者
package com.leon.activemq_helloworld;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消费者
*
* @author leon
*
*/
public class Consumer {
public static void cust(String distinationName) throws Exception {
// 1、创建工厂连接对象,要指定ip和端口号(61616是默认的openwrite端口,在conf/activemq.xml里配置)
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
// 2、使用连接工厂创建一个连接对象
Connection connection = connectionFactory.createConnection();
// 3、开启连接
connection.start();
// 4、使用连接对象创建会话(session)对象
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5、使用会话对象创建目标对象,分为queue(p2p:一对一)和topic(发布订阅:一对多)
Queue queue = session.createQueue(distinationName);
// 6、使用会话对象创建消费者对象
MessageConsumer consumer = session.createConsumer(queue);
// 7、向消费者对象中设置一个messageListener对象,用来监听和接收消息
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
// TODO Auto-generated method stub
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("接收到文本消息:");
System.out.println(textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
// 8、保持程序等待接收消息
System.in.read();
// 9、关闭资源
consumer.close();
session.close();
connection.close();
}
public static void main(String[] args) {
try {
cust(Const.MQ_DISTINATION_NAME);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(4)模拟消息队列p2p过程
a.执行Producer.java的main(): 生产者发布消息 -> ActiveMQ消息队列
访问:http://localhost:8161/admin/ :点击queues选项卡,发现新增了一条消息。
此时,未发送的消息:消费者数量:入队:出队 = 1:0:1:0
b.执行Consumer.java的main(): 消费者接收消息 <- ActiveMQ消息队列
后台打印:
访问:http://localhost:8161/admin/ :点击queues选项卡,发现新增了一位消费者,未发送消息减少了一条,出队数量增加了1。
此时,未发送的消息:消费者数量:入队:出队 = 0:1:1:1
(5)模拟消息队列发布订阅过程
同上,不过要先执行订阅者代码,再执行发布者代码,这样订阅者才能接受到发布者发布的第一条消息,详情参考本文源码:https://github.com/leon2016/activemq-helloworld.git
其实,就是将
Queue queue = session.createQueue(distinationName);
改为
Topic topic = session.createTopic(distinationName);
\---- 未完待续----
TODO 总结activemq与spring整合应用实例。
源码
本文源码:
activemq-helloworld :https://github.com/leon2016/activemq-helloworld.git
参考文献:
activemq官方命令行手册https://www.cnblogs.com/cyfonly/p/6380860.htmlhttps://blog.csdn.net/qq_29963323/article/details/79728581http://www.uml.org.cn/zjjs/201802111.asp