本文主要对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语言开发;是一款非常流行的开源消息服务器.

java mq的总类_消息队列

4.应用场景

(1)异步处理

 如下图,用户注册后,直接响应,邮件和短信发送通过消息队列异步处理。

java mq的总类_jms_02


 对比下图,缩短了响应时间,增加了系统QPS

java mq的总类_jms_03

(2)应用解耦

 如下图,应用消息队列后,库存系统不能正常使用,也不直接影响订单系统。

  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
  • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
  • java mq的总类_ActiveMQ_04

  •  对比下图:传统模式下,库存系统一断无法正常使用,订单系统也将无法正常使用。
  • java mq的总类_java mq的总类_05

(3)流量消峰

 如下图,一个典型应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。这样,用户的请求,服务器接收后,首先写入消息队列。

java mq的总类_消息队列_06


这样做有如下好处:

  • 可以控制活动的人数:假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
  • 可以缓解短时间内高流量压垮应用:秒杀业务根据消息队列中的请求信息,再做后续处理

(4)其他场景
 其他的场景还有:日志处理,消息通讯等。


ActiveMQ安装配置

1.下载安装

本文介绍Mac OS下安装

(1)推荐方式:通过brew安装ActiveMQ

$ brew install activemq

安装完后,通过下列命令查看版本来确定是否安装成功:

$activemq --version

java mq的总类_java mq的总类_07


(2)也可自行下载解压安装:

下载地址:http://activemq.apache.org/components/classic/download/

java mq的总类_java_08


注意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)

java mq的总类_jms_09

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官方命令行手册


ActiveMQ之Hello World 实例

 不知从什么时候起,渐渐习惯这种了解基本的理论后直接快速上手一个hello World实例~~ 不过,这确实是现学现用的一个好方法,但是有时间的话,还是觉得要看看书系统地了解一下,这样,遇到了新问题,也有迹可循。

1.启动activemq访问:http://localhost:8161/admin/

点击queues如图:此时消息队列为空

java mq的总类_jms_10

2.新建maven项目

java mq的总类_ActiveMQ_11


添加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.编写消息生产者与消费者代码

最终目录结构如下:

java mq的总类_jms_12


(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

java mq的总类_java mq的总类_13


b.执行Consumer.java的main(): 消费者接收消息 <- ActiveMQ消息队列

后台打印:

java mq的总类_消息队列_14

访问:http://localhost:8161/admin/ :点击queues选项卡,发现新增了一位消费者,未发送消息减少了一条,出队数量增加了1。
此时,未发送的消息:消费者数量:入队:出队 = 0:1:1:1

java mq的总类_java mq的总类_15

(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