ActiveMQ的安装与简单使用
这学期在软件平台与中间件这门课上学习了消息中间件的内容,这里我以ActiveMQ为例给大家讲解一下ActiveMQ的安装与简单使用,若有不对的地方欢迎指正!
一、ActiveMQ的安装与配置
1、下载安装包
官网下载地址: http://activemq.apache.org/components/classic/download/
根据自己的电脑的系统下载对应的版本.(此处我是Windows版本)
2、解压
到下载压缩包的目录里,解压压缩包
3、启动
进入相对目录: \apache-activemq-5.16.2\bin\win64
点击activemq.bat
成功启动ActiveMQ
4、访问
游览器输入地址: http://127.0.0.1:8161/admin/ 访问
账号和密码默认为: admin/admin
二、ActiveMQ的简介
2.1 ActiveMQ是由Apache出品的一款开源消息中间件,旨在为应用程序提供高效、可扩展、稳定、安全的企业级消息通信。它的设计目标是提供标准的、面向消息的、多语言的应用集成消息通信中间件。ActiveMQ实现了JMS 1.1并提供了很多附加的特性,比如JMX管理、主从管理、消息组通信、消息优先级、延迟接收消息、虚拟接收者、消息持久化、消息队列监控等。
2.2 ActiveMQ应用场景
(1)异步处理
串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。
并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
(2)应用解耦
如下图,应用消息队列后,库存系统不能正常使用,也不直接影响订单系统。
- 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
- 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作(ps:)
对比下图:
传统模式的缺点
- 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;
- 订单系统与库存系统耦合;
(3)流量削峰
如下图,相信大家在网购时都有体验,一个典型的应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。这样,用户的请求,服务器接收后,首先写入消息队列。
这样做有如下好处:- 可以控制活动的人数:假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
- 可以缓解短时间内高流量压垮应用:秒杀业务根据消息队列中的请求信息,再做后续处理
其他的场景还有:日志处理,消息通讯等。
三、ActiveMQ实例
3.1 Queue模式
消息生产者
ProduceQueue.java
package com.fightwk.active;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ProduceQueue {
/**
* 发送消息到ActiveMQ中,具体的消息内容为参数信息
* 开发JMS相关代码中,使用的接口类型都是javax.jms包下的类型
*/
public void sendTextMessage(String datas){
//连接工厂
ConnectionFactory factory = null;
//连接
Connection connection = null;
//目的地
Destination destination = null;
//会话
Session session = null;
//消息发送者
MessageProducer producer = null;
//消息对象
Message message = null;
try{
//创建连接工厂,连接ActiveMQ服务的连接工厂
//创建工厂,构造方法有三个参数,分别是用户名,密码,连接地址
//无参构造,有默认的连接地址。本地连接
//单参数构造,五无验证模式,没有用户的认证
//三参数构造,有认证+指定地址。默认端口是61616.从ActiveMQ的conf/activemq.xml配置文件中查看
factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
//通过工厂,创建连接对象
//创建连接的方法有重载,其中有createConnection(String username,String password)
//可以再创建连接工厂时,只传递连接地址,不传递用户信息
connection = factory.createConnection();
//建议启动连接,消息的发送者不是必须启动连接。消息的消费者必须启动连接
//producer再发送消息的时候,会检查是否启动了连接,如果未启动,自动启动
//如果有特殊的配置,建议配置完毕后再启动连接
connection.start();
//通过连接对象,创建会话对象
/*创建会话的时候,必须传递两个参数,分别代表的是否支持事务和如何确认消息处理
* transacted:是否支持事务,数据类型是boolean.true-支持 false-不支持
* true:支持事务,第二个参数对producer来说默认是无效。建议传递的数据是Session.SESSION_TRANSACTED
* false:不支持事务,常用参数。第二个参数必须传递,且必须有效
* acknowledgeMode:如何确认消息的处理。使用确认机制实现的
* AUTO_ACKNOWLEDGE:自动确认消息。消息的消费者处理消息后,自动确认。常用。商业开发不推荐
* CLIENT_ACKNOWLEDGE:客户端手动确认。消息的消费者处理后,必须手工确认
* DUPS_OK_ACKNOWLEDGE:有副本的客户端手动确认
* 一个消息可以多次处理
* 可以降低Session的消耗,再可以容忍重复消息时使用(不推荐使用)
*/
session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
//创建目的地。参数是目的地名称。是目的地的唯一标记
destination = session.createQueue("fightwk");
//通过会话对象,创建消息的发送者producer
//创建的消息发送者,发送的消息一定到指定的目的地中
//创建producer的时候,可以不提供目的地。在发送消息的时候制定目的地
producer = session.createProducer(destination);
//创建文本消息对象,作为具体数据内容的载体
message = session.createTextMessage(datas);
//使用producer,发送消息到ActiveMQ中的目的地。如果消息发送失败。抛出异常
producer.send(message);
System.out.println("消息已经发送成功....");
}catch(Exception e){
e.printStackTrace();
}finally{
if(producer != null){//回收消息发送者
try {
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session != null){//回收会话对象
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(connection != null){//回收连接对象
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
ProduceQueue producer = new ProduceQueue();
producer.sendTextMessage("我是一个测试的ActiveMQ...");
}
}
消息消费者
ConsumerQueue.java
package com.fightwk.active;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ConsumerQueue {
public String receiveTextMessage(){
String resultCode = "";
ConnectionFactory factory = null;
Connection connection = null;
Session session = null;
Destination destination = null;
//消息的消费者,用于接收消息的对象
MessageConsumer consumer = null;
TextMessage message = null;
try{
factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
connection = factory.createConnection();
//消息的消费者必须启动连接,否支无法处理消息
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("fightwk");
//创建消息消费者对象,在指定的目的地中获取消息
consumer = session.createConsumer(destination);
//获取队列中的消息
message = (TextMessage) consumer.receive();
//处理文本消息
resultCode = message.getText();
}catch(Exception e){
e.printStackTrace();
}finally{
if(consumer != null){//回收消息消费者
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session != null){//回收会话对象
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(connection != null){//回收连接对象
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
return resultCode;
}
public static void main(String[] args){
ConsumerQueue consumer = new ConsumerQueue();
String messageString = consumer.receiveTextMessage();
System.out.print("接收的消息内容是:" + messageString);
}
}
ProduceQueue.java运行截图
ConsumerQueue.java运行截图
3.2 Topic模式
消息生产者
ProducerTopic.java
package com.fightwk.active;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ProducerTopic {
/**
* 发送消息到ActiveMQ中,具体的消息内容为参数信息
* 开发JMS相关代码中,使用的接口类型都是javax.jms包下的类型
* @param datas 消息内容
*/
public void sendTextMessage(String datas){
//连接工厂
ConnectionFactory factory = null;
//连接
Connection connection = null;
//目的地
Destination destination = null;
//会话
Session session = null;
//消息发送者
MessageProducer producer = null;
//消息对象
Message message = null;
try{
//创建连接工厂,连接ActiveMQ服务的连接工厂
//创建工厂,构造方法有三个参数,分别是用户名,密码,连接地址
//无参构造,有默认的连接地址。本地连接
//单参数构造,五无验证模式,没有用户的认证
//三参数构造,有认证+指定地址。默认端口是61616.从ActiveMQ的conf/activemq.xml配置文件中查看
factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
//通过工厂,创建连接对象
//创建连接的方法有重载,其中有createConnection(String username,String password)
//可以再创建连接工厂时,只传递连接地址,不传递用户信息
connection = factory.createConnection();
//建议启动连接,消息的发送者不是必须启动连接。消息的消费者必须启动连接
//producer再发送消息的时候,会检查是否启动了连接,如果未启动,自动启动
//如果有特殊的配置,建议配置完毕后再启动连接
connection.start();
//通过连接对象,创建会话对象
/*创建会话的时候,必须传递两个参数,分别代表的是否支持事务和如何确认消息处理
* transacted:是否支持事务,数据类型是boolean.true-支持 false-不支持
* true:支持事务,第二个参数对producer来说默认是无效。建议传递的数据是Session.SESSION_TRANSACTED
* false:不支持事务,常用参数。第二个参数必须传递,且必须有效
* acknowledgeMode:如何确认消息的处理。使用确认机制实现的
* AUTO_ACKNOWLEDGE:自动确认消息。消息的消费者处理消息后,自动确认。常用。商业开发不推荐
* CLIENT_ACKNOWLEDGE:客户端手动确认。消息的消费者处理后,必须手工确认
* DUPS_OK_ACKNOWLEDGE:有副本的客户端手动确认
* 一个消息可以多次处理
* 可以降低Session的消耗,再可以容忍重复消息时使用(不推荐使用)
*/
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//创建主题目的地。参数是目的地名称。是目的地的唯一标记
destination = session.createTopic("fightwk");
//通过会话对象,创建消息的发送者producer
//创建的消息发送者,发送的消息一定到指定的目的地中
//创建producer的时候,可以不提供目的地。在发送消息的时候制定目的地
producer = session.createProducer(destination);
//创建文本消息对象,作为具体数据内容的载体
message = session.createTextMessage(datas);
//使用producer,发送消息到ActiveMQ中的目的地。如果消息发送失败。抛出异常
producer.send(message);
System.out.println("消息已经发送成功....");
}catch(Exception e){
e.printStackTrace();
}finally{
if(producer != null){//回收消息发送者
try {
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session != null){//回收会话对象
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(connection != null){//回收连接对象
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
System.out.println("生产者程序开始启动.....");
ProducerTopic producer = new ProducerTopic();
producer.sendTextMessage("我是一个测试的ActiveMQ Topic...");
System.out.println("生产者程序已经关闭.....");
}
}
消息消费者代码
ConsumerTopicA.java
package com.fightwk.active;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ConsumerTopicA {
public String receiveTextMessage(){
String resultCode = "";
ConnectionFactory factory = null;
Connection connection = null;
Session session = null;
Destination destination = null;
//消息的消费者,用于接收消息的对象
MessageConsumer consumer = null;
TextMessage message = null;
try{
factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
connection = factory.createConnection();
//消息的消费者必须启动连接,否支无法处理消息
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("fightwk");
//创建消息消费者对象,在制定的目的地中获取消息
consumer = session.createConsumer(destination);
message = (TextMessage) consumer.receive();
resultCode = message.getText();
/**消费者消息监听开始*/
// consumer.setMessageListener(new MessageListener() {
//
// public void onMessage(Message message) {
// try {
// System.out.println("接收消息 = [" + ((TextMessage) message).getText() + "]");
// } catch (JMSException e) {
// e.printStackTrace();
// }
// }
// });
//阻塞当前代码。保证listener代码未结束。如果代码结束了,监听器自动关闭
// System.in.read();
/**消费者消息监听结束*/
}catch(Exception e){
e.printStackTrace();
}finally{
if(consumer != null){//回收消息消费者
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session != null){//回收会话对象
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(connection != null){//回收连接对象
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
return resultCode;
}
public static void main(String[] args){
System.out.println("消费者A程序开始启动.....");
ConsumerTopicA consumerTopicA = new ConsumerTopicA();
String messageString = consumerTopicA.receiveTextMessage();
System.out.println("接收的消息内容是:" + messageString);
System.out.println("消费者A程序已经关闭.....");
}
}
ConsumerTopicB.java
package com.fightwk.active;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ConsumerTopicB {
public String receiveTextMessage(){
String resultCode = "";
ConnectionFactory factory = null;
Connection connection = null;
Session session = null;
Destination destination = null;
//消息的消费者,用于接收消息的对象
MessageConsumer consumer = null;
TextMessage message = null;
try{
factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
connection = factory.createConnection();
//消息的消费者必须启动连接,否支无法处理消息
connection.start();
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("fightwk");
//创建消息消费者对象,在制定的目的地中获取消息
consumer = session.createConsumer(destination);
message = (TextMessage) consumer.receive();
resultCode = message.getText();
}catch(Exception e){
e.printStackTrace();
}finally{
if(consumer != null){//回收消息消费者
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(session != null){//回收会话对象
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if(connection != null){//回收连接对象
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
return resultCode;
}
public static void main(String[] args){
System.out.println("消费者B程序开始启动.....");
ConsumerTopicA consumerTopicA = new ConsumerTopicA();
String messageString = consumerTopicA.receiveTextMessage();
System.out.println("接收的消息内容是:" + messageString);
System.out.println("消费者B程序已经关闭.....");
}
}
运行消息消费者代码
(ps:切记在Topic模式中一定要先运行消费者在运行生产者,不信的话可以自己试试~)
运行生产者代码
ActiveMQ网站
这里是作别CSDN博客一年之久的fightwk,从今天起会尽量更新作为巩固知识点的一个途径。