1.为什么使用消息队列
异步 ,解耦 ,削峰
解耦
传统模式 系统耦合性太高,每一个系统的接入都需要系统A修改代码
好处: 不需要系统A修改代码了 ,将消息写入消息队列中,需要什么消息可以自己去取
异步:
缺点:一些非必要的业务逻辑 以同步的方式运行,浪费时间
将耗时的操作放入消息队列 ,以异步的方式执行 ,节省时间
削峰
当数据量大时,直接怼到服务器 ,服务器撑不住就挂了
优点:服务器安装能承受的量慢慢的去消息队列中 抽取
消息队列的缺点
- 系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低
- 系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。
参考博文
作者:孤独烟
孤独烟 - 博客园rjzheng.cnblogs.com学习主题:ActiveMQ
学习目标:
1 掌握什么是MQ 什么是JMS
2 掌握ActiveMQ的安装与目录结构
对应视频:
http://www.itbaizhan.cn/course/id/85.html
对应文档:
无
对应作业
1. ActiveMQ简介
(1) 什么是ActiveMQ?
Apache出品 ,最流行的开源消息总线 ,
完全支持JMS和J2EE规范的JMS Provider实现
(2) 什么是消息?
消息是两台计算机间传送的数据单位
(3) 什么是队列?
(4) 什么是消息队列?
是消息的传输过程中保存消息的容器
(5) 常见的消息服务有哪些?
ActiveMQ 和 RabbitMQ 和RocketMQ
2. 消息服务应用场景
(1) 消息服务可应用于哪些场景?
主要特点是异步请求 ,主要目的是减少请求响应时间和解耦
将比较耗时的操作放入消息队列中处理 , 接收方也只需要去消息队列中去取消息
应用的解耦 流量的削峰
3. JMS消息模型介绍
(1) 什么是JMS?
Java平台上有关消息中间件的技术规范
(2) JMS中有哪些消息模型?
点对点模型 生产者发送一条信息到queue,只有一个消费者能收到
发布订阅模型 发布者发送到topic的消息,只要订阅者才会收到消息
4. 安装ActiveMQ
(1) 如何安装ActiveMQ?
下载ActiveMQ安装包
ActiveMQ5.10.x 以上版本必须使用 JDK1.8 才能正常使用。
ActiveMQ5.9.x 及以下版本使用 JDK1.7 即可正常使用。
上传至Linux服务器
解压安装文件 tar -zxf apache-activeMQ
如果权限不足,则无法执行,需要修改文件权限
Chmod 755 activemq
复制到/usr/local目录下 cp 文件 /usr/local -r
启动activemq
/usr/local/activemq/bin/activemq start
检查进程
Ps aux|grep activemq
如上内容启动成功
访问管理界面 默认登录用户和密码都是admin
5. ActiveMQ目录介绍
(1) ActiveMQ有哪些目录?没给目录的作用是什么?
Bin 存放的是脚本文件
Conf 存放的是基本配置文件
Data 存放的是日志文件
Docs 存放的是说明文档
Examples 存放的是简单的实例
Lib 存放的是activemq所需的jar'包
Webapps 用于存放项目的目录
分享/讲解/扩展思考
点名提问从第一节课到最后一节课分别学到了什么,直到同学们把所有的知识点都说出来并且保证无误。
第172次(ActiveMQ)
学习主题:ActiveMQ
学习目标:
1 掌握ActiveMQ中的术语与API
2 掌握使用MQ处理文本消息,对象消息
3 掌握使用ActiveMQ实现队列服务监听处理消息
4 掌握Topic模型
对应视频:
http://www.itbaizhan.cn/course/id/85.html
对应文档:
无
对应作业
6. ActiveMQ中的术语
(1) Destination是什么含义?
目的地 ,JMS Provider(消息中间件)复制维护,用于对Message进行管理的对象
MessageProducer需要指定Destination才能发送消息,
MessageReceiver需要指定Destination才能接受消息
(2) Producer是什么含义?
消息生产者,负责发送Message到目的地
(3) Consumer | Receiver是什么含义?
消息消费者,负责从目的地中消费[处理|监听|订阅]Message
(4) Message是什么含义?
消息,存放一次消息的载体
7. ActiveMQ中的常用API
(1) ConnectionFactory的作用是什么?
链接工厂,用来生产链接
(2) Connection的作用是什么?
链接.用于建立访问Activemq链接的类型
(3) Session的作用是什么?
会话 一次持久有效有状态的访问,由链接创建
(4) Destination & Queue的作用是什么?
目的地,用户发送信息和取信息的载体
(5) MessageProducer的作用是什么?
消息生产者 发送消息给activemq服务的工具
(6) MessageConsumer的作用是什么?
消息的接收者 用于从Activemq服务中获取消息的工具 ,
(7) Message的作用是什么?
发送消息和接受消息的数据载体
8. ActiveMQ处理文本消息-创建消息生产者
(1) 如何创建消息生产者?
package com.bjsxt.provider;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
public class HelloProvider {
public static void main(String[] args) {
sendHelloactiveMq("Hello World");
}
//生产消息
public static void sendHelloactiveMq(String msg) {
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageProducer producer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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(msg);
//发送消息
producer.send(message);
//回收消息发送者资源
if (connection !=null) {
connection.close();
}
if (session!=null) {
session.close();
}
if (producer !=null) {
producer.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
9. ActiveMQ处理文本消息-创建消息消费者
(1) 如何创建消息消费者?
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageConsumer consumer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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("消息消费者消费了消息:"+msg);
//回收资源
if (connection !=null) {
connection.close();
}
if (session!=null) {
session.close();
}
if (consumer !=null) {
consumer.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
10. ActiveMQ处理对象消息
(1) 如何处理消息对象?
只需更改这一部分
//创建消息的生产者
producer = session.createProducer(destination);
//创建消息对象
message=session.createObjectMessage(user);
//发送消息
producer.send(message);
//处理消息消费者
ObjectMessage objMessage = (ObjectMessage)message;
User user = (User)objMessage.getObject();
System.out.println(user.toString());
11. ActiveMQ实现队列服务监听处理消息
(1) ActiveMQ实现队列服务监听处理消息
Provider
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageProducer producer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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("my-Users");
//创建消息的生产者
producer = session.createProducer(destination);
//创建消息对象
message=session.createObjectMessage(user);
//发送消息
producer.send(message);
//回收消息发送者资源
if (connection !=null) {
connection.close();
}
if (session!=null) {
session.close();
}
if (producer !=null) {
producer.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
Consumer:设置了监听,有对应消息上传时就会自动消费 回调函数 链接不能关闭 且consumer先启动 (观察者模式)
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageConsumer consumer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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("my-Users");
//创建消息的消费者
consumer =session.createConsumer(destination);
//创建消息对象
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
// TODO Auto-generated method stub
//处理消息
ObjectMessage objMessage = (ObjectMessage)message;
User user=null;
try {
user = (User)objMessage.getObject();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user.toString());
}
});
}catch (Exception e) {
e.printStackTrace();
}
}
12. Topic模型的使用
(1) 什么是Topic模型?
Publish/Subscribe处理模式(Topic)
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息
和点对点方式不同,发布到topic的消息会被所有订阅者消费
当生产者发布消息,不管是否有消费者,都不会保存消息
一定要先有消费的消费者,后有消息的生产者
创建生产者
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageProducer producer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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.createTopic("topic-user");
//创建消息的生产者
producer = session.createProducer(destination);
//创建消息对象
message=session.createObjectMessage(user);
//发送消息
producer.send(message);
//回收消息发送者资源
if (connection !=null) {
connection.close();
}
if (session!=null) {
session.close();
}
if (producer !=null) {
producer.close();
}
}catch (Exception e) {
e.printStackTrace();
}
创建消费者
public void consumer() {
//定义链接工厂
ConnectionFactory connectionFactory = null;
//定义链接对象
Connection connection = null;
//定义会话
Session session = null;
//目的地
Destination destination = null;
//定义消息的发送者
MessageConsumer consumer =null;
//定义消息
Message message = null;
try{
//创建链接工厂
/*
* userName:访问ActiveMQ服务的用户名, 默认为admin .用户名可以通过jetty-ream.properties文件进行修改
* password:访问ActiveMQ服务的用户密码 默认为admin 密码可以通过jetty-ream.properties文件进行修改
*
* brokerURL:访问ActiveMQ服务的路径地址 路径结构为:协议名://主机地址:端口号
* */
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.93.128:61616");
//通过链接工厂创建链接对象
connection = connectionFactory.createConnection();
//启动链接
connection.start();
/*
* 创建会话
* tranxacted :是否使用事务.可选值为 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.createTopic("topic-user");
//创建消息的消费者
consumer =session.createConsumer(destination);
//创建消息对象
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
// TODO Auto-generated method stub
//处理消息
ObjectMessage objMessage = (ObjectMessage)message;
User user=null;
try {
user = (User)objMessage.getObject();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user.toString());
}
});
}catch (Exception e) {
e.printStackTrace();
}
}