ActiveMQ
ActiveMQ简介
ActiveMQ 是 Apache 出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个
完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现,尽管 JMS 规范出台已经是很久
的事情了,但是 JMS 在当今的 J2EE 应用中间仍然扮演着特殊的地位。
什么是消息
“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;
也可以更复杂,可能包含嵌入对象。
什么是队列
什么是消息队列
“消息队列”是在消息的传输过程中保存消息的容器
常用消息服务应用
- ActiveMQ
ActiveMQ 是 Apache 出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现。 - RabbitMQ
RabbitMQ 是一个在 AMQP 基础上完成的,可复用的企业消息系统。他遵循 Mozilla Public License 开源协议。开发语言为 Erlang - RocketMQ
由阿里巴巴定义开发的一套消息队列应用服务
消息服务的应用场景
消息队列的主要特点是异步处理,主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和
异步处理
用户注册
用户注册流程:
- 注册处理以及写数据库
- 发送注册成功的手机短信
- 发送注册成功的邮件信息
如果用消息中间件:则可以创建两个线程来做这些事情,直接发送消息给消息中间件,
然后让邮件服务和短信服务自己去消息中间件里面去取消息,然后取到消息后再自己做对应的业务操作。就是这么方便
应用的解耦
订单处理
生成订单流程:
- 在购物车中点击结算
- 完成支付
- 创建订单
- 调用库存系统
订单完成后,订单系统并不去直接调用库存系统,而是发送消息到消息中间件,写入一
个订单信息。库存系统自己去消息中间件上去获取,然后做发货处理,并更新库存,这样能
够实现互联网型应用追求的快这一个属性。而库存系统读取订单后库存应用这个操作也是非
常快的,所以有消息中间件对解耦来说也是一个不错的方向。
流量的削峰
秒杀功能
秒杀流程:
- 用户点击秒杀
- 发送请求到秒杀应用
- 在请求秒杀应用之前将请求放入到消息队列
- 秒杀应用从消息队列中获取请求并处理。
比如,系统举行秒杀活动,热门商品。流量蜂拥而至 100 件商品,10 万人挤进来怎么
办?10 万秒杀的操作,放入消息队列。秒杀应用处理消息队列中的 10 万个请求中的前 100
个,其他的打回,通知失败。流量峰值控制在消息队列处,秒杀应用不会瞬间被怼死.
JMS
JMS(Java Messaging Service)是 Java 平台上有关面向消息中间件的技术规范,它便于
消息系统中的 Java 应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接
口,简化企业应用的开发。
点对点模型(Point To Point)
生产者发送一条消息到 queue,只有一个消费者能收到。
发布订阅模型(Publish/Subscribe)
发布者发送到 topic 的消息,只有订阅了 topic 的订阅者才会收到消息
ActiveMQ 安装
可在ActiveMQ 官网下载安装包: http://activemq.apache.org
注意:
- ActiveMQ5.10.x 以上版本必须使用 JDK1.8 才能正常使用。
- ActiveMQ5.9.x 及以下版本使用 JDK1.7 即可正常使用
解压安装文件
tar -zxf apache-activemq-5.9.0-bin.tar.gz
复制应用至本地目录
cp -r apache-activemq-5.9.0 /usr/local/activemq
启动 ActiveMQ
/usr/local/activemq/bin/activemq start
如果activemq没有执行文件权限,需要通过chmod命令进行添加
测试 ActiveMQ
ps aux | grep activemq
见到下述内容即代表启动成功
管理界面
使用浏览器访问 ActiveMQ 管理应用, 地址如下:
http://ip:8161/admin/
用户名: admin
密码: admin
修改访问端口:
vim /usr/local/activemq/conf/jetty.xml
修改用户名和密码
vim /usr/local/activemq/conf/users.properties
然后将下面的键值对
改成
自定义用户名:自定义密码
即可
注意:无论是修改端口还是修改用户名密码,都需要将activemq重启后才会生效
重启 ActiveMQ
/usr/local/activemq/bin/activemq restart
关闭 ActiveMQ
/usr/local/activemq/bin/activemq stop
配置文件 activemq.xml
配置文件中,配置的是 ActiveMQ 的核心配置信息. 是提供服务时使用的配置. 可以修改
启动的访问端口. 即 java 编程中访问 ActiveMQ 的访问端口.
默认端口为 61616.
使用协议是: tcp 协议.
修改端口:
vim /usr/local/activemq/conf/activemq.xml
修改端口后, 保存并重启 ActiveMQ 服务即可.
ActiveMQ 目录介绍
- bin 存放的是脚本文件
- conf 存放的是基本配置文件
- data 存放的是日志文件
- docs 存放的是说明文档
- examples 存放的是简单的实例
- lib 存放的是 activemq 所需 jar 包
- webapps 用于存放项目的目录
ActiveMQ 术语
Destination
目的地,JMS Provider(消息中间件)负责维护,用于对 Message 进行管理的对象。
MessageProducer 需要指定 Destination 才能发送消息,MessageReceiver 需要指定 Destination才能接收消息。
Producer
消息生成者,负责发送 Message 到目的地。
Consumer | Receiver
消息消费者,负责从目的地中消费【处理|监听|订阅】Message
Message
消息,消息封装一次通信的内容
ActiveMQ 应用
ActiveMQ 常用 API 简介
- ConnectionFactory:链接工厂, 用于创建链接的工厂类型
- Connection:链接. 用于建立访问 ActiveMQ 连接的类型, 由链接工厂创建
- Session:会话, 一次持久有效有状态的访问. 由链接创建
- Destination & Queue:
目的地, 用于描述本次访问 ActiveMQ 的消息访问目的地. 即 ActiveMQ 服务中的具体队列. 由会话创建.
interface Queue extends Destination·
- MessageProducer:
消息消费者【消息订阅者,消息处理者】, 在一次有效会话中, 用于从 ActiveMQ 服务中获取消息的工具. 由会话创建. - Message:
消息, 通过消息生成者向 ActiveMQ 服务发送消息时使用的数据载体对象或消息消费者从 ActiveMQ 服务中获取消息时使用的数据载体对象. 是所有消息【文本消息,对象消息等】具体类型的顶级接口. 可以通过会话创建或通过会话从 ActiveMQ 服务中获取.
ActiveMQ简单实例
- pom.xml依赖
<!--https://mvnrepository.com/artifact/org.apache.activemq/activemq-all --> <dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.9.0</version>
</dependency>
- 消息生产者(第一个maven项目)
public class HelloWorldProducer {
public void sendHelloWorldActiveMQ(String
msgTest){
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageProducer producer = null;
//定义消息
Message message = null;
try{
/**
* userName:访问 ActiveMQ 服务的用户名。用户密码。默认的为 admin。
* password:访问 ActiveMQ 服务的用户名。用户密码。默认的为 admin。
* brokerURL:访问 ActiveMQ 服务的路径地址。路径结构为:协议名://主机地址:端口号
*/
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.70.151:61616");
//创建连接对象
connection =
connectionFactory.createConnection();
//启动连接
connection.start();
/**
* transacted:是否使用事务 可选值为:true|false
* true:使用事务 当设置此变量值。Session.SESSION_TRANSACTED
* false:不适用事务,设置此变量则 acknowledgeMode 参数必须设置
* acknowledgeMode:
* Session.AUTO_ACKNOWLEDGE:自动消息确认
机制
* Session.CLIENT_ACKNOWLEDGE:客户端确认
机制
* Session.DUPS_OK_ACKNOWLEDGE:有副本的客
户端确认消息机制
*/
session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
//创建目的地,目的地名称即队列的名称。消息的消费者需要通过此名称访问对应的队列
destination = session.createQueue("helloworld-destination");
//创建消息的生产者
producer = session.createProducer(destination);
//创建消息对象
message = session.createTextMessage(msgTest);
//发送消息
producer.send(message);
}catch(Exception e){
e.printStackTrace();
}finally{
//回收消息发送者资源
if(producer != null){
try {
producer.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(session != null){
try {
session.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
- 消息消费者(第二个maven项目)
public class HelloWorldConsumer {
public void readHelloWorldActiveMQ() {
// 定义链接工厂
ConnectionFactory connectionFactory = null;
// 定义链接对象
Connection connection = null;
// 定义会话
Session session = null;
// 目的地
Destination destination = null;
// 定义消息的发送者
MessageConsumer consumer = null;
// 定义消息
Message message = null;
try {
/**
* userName:访问 ActiveMQ 服务的用户名。默认的为 admin。
* password:访问 ActiveMQ 服务的用户名。默认的为 admin。
* brokerURL:访问ActiveMQ 服务的路径地址。
* 路径结构为:协议名://主机地址:端口号
*/
connectionFactory = new
ActiveMQConnectionFactory("admin", "admin","tcp://192.168.70.151:61616");
// 创建连接对象
connection = connectionFactory.createConnection();
// 启动连接
connection.start();
/**
* transacted:是否使用事务 可选值为:
* true|false
* true:使用事务,当设置此变量值。Session.SESSION_TRANSACTED
* false:不适用事务,设置此变量, 则acknowledgeMode 参数必须设置
* acknowledgeMode:
* Session.AUTO_ACKNOWLEDGE:自动消息确认机制
* Session.CLIENT_ACKNOWLEDGE:客户端确认机制
* Session.DUPS_OK_ACKNOWLEDGE:有副本的客户端确认消息机制
*/
session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// 创建目的地,目的地名称即队列的名称。消息的消费者需要通过此名称访问对应的队列
destination = session.createQueue("helloworld-destination");
// 创建消息的消费者
consumer =session.createConsumer(destination);
// 创建消息对象
message = consumer.receive();
//处理消息
String msg = ((TextMessage)message).getText();
System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 回收消息发送者资源
if (consumer != null) {
try {
consumer.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
- 测试
producer
public class Test {
public static void main(String[] args) {
HelloWorldProducer producer = new HelloWorldProducer();
producer.sendHelloWorldActiveMQ("HelloWorld");
}
}
consumer
public class Test {
public static void main(String[] args) {
HelloWorldConsumer consumer = newHelloWorldConsumer();
consumer.readHelloWorldActiveMQ();
}
}
ActiveMQ处理对象信息
能被处理的对象的类必须实现Serializable接口
- 在生产者代码中的变化
//上面简单实例处理字符串信息时:
// message = session.createTextMessage(msgTest);
//处理对象信息时
//创建消息对象
message = session.createObjectMessage(users);
- 在消费者代码中的变化
//上面简单实例处理字符串信息时:
//String msg = ((TextMessage)message).getText();
//处理对象信息时
// 创建消息对象
message = consumer.receive();
//处理消息
ObjectMessage objMessage = (ObjectMessage)message;
Users users = (Users)objMessage.getObject();
JMS - 实现队列服务监听
- 在生产者代码中无变化
- 在消费者代码中的变化
//上面简单实例时:
//String msg = ((TextMessage)message).getText();
//队列服务
consumer.setMessageListener(new MessageListener() {
//ActiveMQ 回调的方法。通过该方法将消息传递到 consumer
@Override
public void onMessage(Message message) {
//处理消息
String msg=null;
try {
msg = ((TextMessage)message).getText();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
}
});
Publish/Subscribe 处理模式(Topic)
- 在生产者代码中的变化
//上面简单实例时:
//destination = session.createQueue("helloworld-destination");
//Publish/Subscribe 处理模式
destination = session.createTopic("test-topic");
- 在消费者代码中的变化
//上面简单实例时:
//destination = session.createQueue("helloworld-destination");
//String msg = ((TextMessage)message).getText();
//Publish/Subscribe 处理模式
destination = session.createTopic("test-topic");
consumer.setMessageListener(new MessageListener() {
//ActiveMQ 回调的方法。通过该方法将消息传递到 consumer
@Override
public void onMessage(Message message) {
//处理消息
String msg=null;
try {
msg = ((TextMessage)message).getText();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
}
});
//生产者类中重写了run方法
@Override
public void run() {
this.readHelloWorldActiveMQ();
}