ActiveMQ学习文档
MQ(Message Queue)=消息中间件,是理念(规范)activeMq是其中一个具体的产品
MQ学习前言
消息
微信消息,短信消息,邮件消息,语音留言消息…
双方约定好的格式作为一种信息载体的传输媒介,在网上发布然后让大家获得,实现信息的同步和信息 的交换
中间件
既然为中间件,那么一定会分为左中右
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dyYqE2d9-1585967767084)(image/2019-06-26_100759.png)]
MQ的分类
消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ,炙手可热的Kafka,阿里巴巴自主开发RocketMQ等
Kafka
RabbitMQ
RocketMQ
ActiveMQ
…
1.什么是消息中间件
可与OA、ERP集成的免费消息中间件Active Messenger(简称AM)是一款非常实用的企业即时通讯软件。系统提供免费的消息中间件,开放给第三方程序使用。
2.简介
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
3.**JMS(Java Message Service)**的概念
它是一种与厂商无关的API,用来访问消息收发系统消息。它类似于JDBC,JDBC是可以用来访问不同关系数据库的API,而JMS则提供同样与厂商无关的访问消息收发服务的方法,这样就可以通过消息收发服务实现从一个JMS客户机向另一个JMS客户机发送消息,所需要的是厂商支持JMS。换句话说,JMS是Java平台上有关面向消息中间件的技术规范。
4.jms中关键的对象
- ConnectionFactory 连接工厂
- Connection
- session,是发送和接收消息的上下文,用于创建消息生产者,消息消费者,相比rocketMQ会话session是提供事务性的;
- destination,指定生产消息的目的地和消费消息的来源对象;生产者、消费者,由会话创建的对象,顾名思义。
5.消息机制
5.1.p2p(点对点)
- 一个消息只有一个消费者(消息消费即删除)
- 发送者和消费者时间上没有依赖性
- 接收消息之后需向队列发送回执信息
- 目的地必须是队列
契合应用场景:订单和商品,应用解耦
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAKwR6Qy-1585967767086)(image\2.png)]
5.2.pub and sub(发布订阅)
- 一个消息有多个消费者
- 时间上有依赖性
契合应用场景:app的消息推送
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIqvqCCr-1585967767087)(image\3.png)]
6.消息中间件的应用场景
- 应用解耦
- 异步处理
- 流量削峰
- 消息推送
7.ActiveMQ的引言
ActiveMQ是由Apache出品的,一款最流行的,能力强劲的消息中间件(MOM:Message Orient middleware)。并且是对消息通信规范JMS的一种具体实现
8.安装ActiveMQ
8.1.搭建java环境 安装jdk
8.1.a.安装jdk(1) yum一键安装
8.1.1.首先执行以下命令查看可安装的jdk版本:
yum -y list java*
8.1.2.选择自己需要的jdk版本进行安装,比如这里安装1.8,执行以下命令:
yum install -y java-1.8.0-openjdk-devel.x86_64
等待安装完成即可。
8.1.3.安装完成之后,查看安装的jdk版本,输入以下指令:
java -version
8.1.b.安装jdk(2) 手动安装 使用过
8.1.1.将jdk导入SFTP
8.1.2.解压压缩包
tar -zxvf jdk-8u171-linux-x64.tar.gz
8.1.3.将解压文件移动到 /usr/local/src/ 目录
mv jdk1.8.0_171 /usr/local/src/
8.1.4.修改配置文件
vim /root/.bashrc
8.1.5.配置环境变量,输入以下指令进行配置:
export JAVA_HOME=/usr/local/src/jdk1.8.0_171
export PATH=JAVA_HOME/bin:PATH
export CLASSPATH=.:JAVA_HOME/lib/dt.jar:JAVA_HOME/lib/tools.jar
8.1.6.重新加载文件
source /root/.bashrc
8.1.c.安装jdk(3) 手动安装
8.1.1.将jdk导入SFTP
8.1.2.解压压缩包
tar -zxvf jdk-8u171-linux-x64.tar.gz
8.1.3.将解压文件移动到 /usr/local/jdk/ 目录,jdk目录需要自己手动创建
mv jdk1.8.0_171 /usr/local/jdk/
8.1.4.修改配置文件
vim /etc/profile
8.1.5.配置环境变量,输入以下指令进行配置:
export JAVA_HOME=/usr/local/jdk/jdk1.8.0_171
export CLASSPATH=$:CLASSPATH:$JAVA_HOME/lib/
export PATH=$PATH:$JAVA_HOME/bin
8.1.6.重新加载文件
source /etc/profile
8.2.安装ActiveMq
1.官网下载最新版本
2…将ActiveMQ导入SFTP
3.解压activemq压缩包
tar -zxvf apache-activemq-5.15.3-bin.tar.gz
4.将解压文件移动到 /usr/local/src/ 目录
mv apache-activemq-5.15.3 /usr/local/src/
5.修改映射文件 将主机名映射本机的ip 默认不用改
vi /etc/sysconfig/network
vi /etc/hosts
加入 主机名映射 192.168.241.142 localhost
重启完成后用,命令查询系统主机名
hostname
6.启动ActiveMQ 进入ActiveMQ安装的bin目录
./activemq start
进入 data 目录
tail -f activemq.log 查看启动日志
7.访问web的控制界面
http://192.168.31.169:8161/admin
默认账号:admin 密码: admin
修改登录用户名密码:
#修改conf目录下的 users.properties
vim users.properties
注意:
- 在外部访问tomcat需要关闭防火墙 service iptables stop(centos6)
- 在外部访问tomcat需要关闭防火墙 (centos7)
临时关闭 systemctl stop firewalld
禁止开机启动 systemctl disable firewalld
8.3.测试访问web的控制界面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0CzvGxe-1585967767088)(image/4.png)]
9.java操作ActiveMQ
9.1.点对点(P2P)
9.1.1.生产者
1.相关依赖
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.7.0</version>
</dependency>
2.开发步骤
@Test
public void testProduct() throws Exception {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
//4.创建生产者
Destination destination=new ActiveMQQueue("javaQueue");
MessageProducer producer = session.createProducer(destination);
//5.创建消息
TextMessage textMessage = session.createTextMessage();
textMessage.setText("hello aaa");
//6.使用生产者发生消息
producer.send(textMessage);
//7.提交
session.commit();
//8.关闭
producer.close();
session.close();
connection.close();
}
9.1.2.消费者
开发步骤
//消费者
@Test
public void testConsumer() throws JMSException {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
Connection connection = activeMQConnectionFactory.createConnection();
connection.start(); //启动连接
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
//4.创建消费者
Destination destination=new ActiveMQQueue("javaQueue");
MessageConsumer consumer = session.createConsumer(destination);
//6.使用消费者发消息
TextMessage message = (TextMessage)consumer.receive();
String text = message.getText();
System.out.println(text);
//7.提交
session.commit();
//8.关闭
consumer.close();
session.close();
connection.close();
}
//消费者
@Test
public void testConsumer() throws JMSException {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
Connection connection = activeMQConnectionFactory.createConnection();
connection.start(); //启动连接
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
Session session = connection.cr
//4.创建生产者
Destination destination=new ActiveMQQueue("javaQueue");
MessageConsumer consumer = session.createConsumer(destination);
//6.使用生产者发生消息
while(true){
TextMessage message = (TextMessage)consumer.receive();
String text = message.getText();
if(message!=null){
System.out.println(text);
//7.提交
session.commit();
}else{
break;
}
}
}
注意:当生产者生产消息之后,不管何时,只要消费者启动,就会接收到消息(非时间依赖性)
9.1.3.发送复杂数据
5种消息体格式
TextMessage: 普通字符转消息,包含一个String (重点)
MapMessage: 一个Map类型的消息,key为String类型,而值为java的基本类型 (重点)
BytesMessage: 二进制数组消息,包含一个byte[]
StreamMessage: Java数据流消息,用标准流操作顺序的填充和读取
ObjectMessage: 对象消息,包含一个可序列化的Java对象
发送和接受的消息体类型必须一致对应
生产者
创建对象
//注意:对象一定要实现Serializable接口
public class User implements Serializable {
private String name;
private String password;
private Integer age;
private Date bir;
}
消息类型
JAVA消息服务定义了6种JMS提供者必须支持的Message接口类型。尽管JMS定义了Message接口,但它并未定义它们的实现方式。这就允许提供者以它们自己的方式实现和传送消息,同时为JMS应用程序开发者维护了一个兼容的标准接口。这6个消息接口是Message和它的5个子接口:TextMessage、StreamMessage、MapMessage、ObjectMessage和ByteMessage。
Message
最简单的消息类型是javax.jms.Message,它担当了其他消息类型基接口(base interface)的角色。可按一下方式创建Message类型,并将它用作一条不含有效负载的JMS消息:
//使用者发布者创建并发送消息
Message message = pubSession .createTextMessage();
publisher.publish(message );
//接受消息
@Override
public void onMessage(Message message) {}
这种类型的消息仅仅包含了JMS消息头和消息属性,而且,它仅限用于事件通知,它仅用于事件通知。一个事件通知就是出现某些情况的一个广播、告警或者通知。如果业务场景需要一个不含有效负载的简单通知,轻量级消息类型就是实现它的最有效方式。
例如为了广播通知某个特定类中的一个异常,你可以发布一条不含有效负载的异常文本消息,如下所示:
//发送异常
try{
}catch(Exception up){
Message message = Sesstion.createMessage();
message.setStringProperty("Exception",up.getMessage());
publisher.publish(message);
throw up;
}
//接受异常
public void onMessage(Message message){
System.out.println("Exception:"+message.getStringProperty());
}
发送消息对象
ObjectMessage
对象消息可以使用Session接口中的两个工厂方法中的一个来创建。
//对象类型的数据
ObjectMessage objectMessage = session.createObjectMessage();
User user = new User("1","xiaohei",23,new Date());
objectMessage.setObject(user);
producer.send(objectMessage);
消息响应
ObjectMessage message = (ObjectMessage) consumer.receive();
User user = (User) message.getObject();
System.out.println(user);
BytesMessage
这种类型懈怠了一组原始类型字节流作为有效负载。
它可以使用应用程序的本机格式来交换数据,这种格式可能不兼容其他现有的Message类型。当JMS纯粹用于两个系统之间的消息传送时,也可以使用这种类型,而且该消息的有效负载对JMS客户端来说是透明的。
如果你使用java.io.DataInputStream和java.io.DataOutputStream,那么以这些I/O类为基础的ByteMessage接口方法,就会让你觉得熟悉。
//Byte类型的数据
BytesMessage bytesMessage = session.createBytesMessage();
bytesMessage.writeByte((byte)12);
bytesMessage.writeShort((short)12);
bytesMessage.writeInt(12);
bytesMessage.writeLong(1212);
bytesMessage.writeFloat(12.12f);
bytesMessage.writeDouble(8.88);
bytesMessage.writeBoolean(true);
bytesMessage.writeChar('a');
bytesMessage.writeUTF("嘿嘿");
producer.send(bytesMessage);
ByteMessage是可移植性最好的而一种消息类型,因此他在于非JMS消息类型传送客户端通信时非常有用。有些情况下,一个JMS客户端可以是一类路由器,它消费来自某个消息源的消息,并将他们传送给一个目的地。路由选择应用程序可能不需要知道它们传输的数据内容,并因此可以选择使用ByteMessage将有效负载作为二进制数据,从一个位置传送到其他位置。
消息响应
BytesMessage message = (BytesMessage ) consumer.receive();
byte b = message.readByte();
short i = message.readShort();
int i1 = message.readInt();
long l = message.readLong();
float v1 = message.readFloat();
double v = message.readDouble();
boolean aBoolean = message.readBoolean();
char c = message.readChar();
String s = message.readUTF();
MapMessage
这种类型携带了一组键/值对作为有效负载。有效负载类似与一个Java.util.Properties对象,除了有效负载值必须是Java原始数据类型和Strings型之外。
本质上,MapMessage的工作方式类似于JMS属性:任何键/值对都可以写入有效负载。其中名称必须是一个String对象,而值则可以是String类型或原始数据类型。
//Map类型的数据
MapMessage mapMessage = session.createMapMessage();
mapMessage.setByte("a",(byte)15);
mapMessage.setBoolean("b",false);
mapMessage.setString("c","哈哈");
mapMessage.setDouble("d",89.9);
mapMessage.setInt("e",23);
producer.send(mapMessage);
消息响应
MapMessage message = (MapMessage ) consumer.receive();
int age = message.getInt("e");
float weight = message.getFloat("Weight");
String name = message.getString("c");
Double height = (Double)message.getObject("d");
System.out.println(message);
StreamMessage
StreamMesage携带了一个Java原始数据类型流作为有效负载。它提供了一套将格式化字节流映射为Java原始数据类型的简便方法。
StreamMessage保持了写入流时的顺序和原始数据类型,因此它适用于形式转换规则。
9.2.发布订阅(pub& sub)
9.2.1.发布者(publisher)
//发布者1
@Test
public void testPub() throws Exception {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
TopicConnection topicConnection = connectionFactory.createTopicConnection();
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
//4.创建生产者
ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
TopicPublisher publisher = topicSession.createPublisher(javaTopic);
//5.创建消息
TextMessage textMessage = topicSession.createTextMessage();
textMessage.setText("hello subscriber");
//6.使用生产者发生消息
publisher.send(textMessage);
//7.提交
topicSession.commit();
//8.关闭
publisher.close();
topicSession.close();
topicConnection.close();
}
9.2.2.订阅者(subscriber)
//订阅者1
@Test
public void testsub1() throws Exception {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
TopicConnection topicConnection = activeMQConnectionFactory.createTopicConnection();
topicConnection.start(); //启动连接
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
//4.创建消费者
ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
TopicSubscriber durableSubscriber = topicSession.createSubscriber(javaTopic);
while(true){
TextMessage message = (TextMessage)durableSubscriber.receive();
String text = message.getText();
if(message!=null){
System.out.println("订阅者1=="+text);
//7.提交
topicSession.commit();
}else{
break;
}
}
}
//订阅者2
@Test
public void testsub2() throws JMSException {
String brokeURL="tcp://192.168.31.169:61616";
//1.连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokeURL);
//2.创建连接
TopicConnection topicConnection = activeMQConnectionFactory.createTopicConnection();
topicConnection.start(); //启动连接
//3.创建会话
//第一个参数 代表第二参数是否生效开启事务 参数二 自动发送回执
TopicSession topicSession = topicConnection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
//4.创建消费者
ActiveMQTopic javaTopic = new ActiveMQTopic("javaTopic");
TopicSubscriber durableSubscriber = topicSession.createSubscriber(javaTopic);
while(true){
TextMessage message = (TextMessage)durableSubscriber.receive();
String text = message.getText();
if(message!=null){
System.out.println("订阅者2=="+text);
//7.提交
topicSession.commit();
}else{
break;
}
}
}
注意:订阅者要在发布者之前启动,才会接收到发布者发布的消息(时间依赖性)
10.springboot集成ActiveMQ
10.1.点对点点对点(P2P)
导入相关依赖
<!--ActiveMQ相关jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
配置文件
server.port=8989
server.servlet.context-path=springbootamq
spring.activemq.broker-url=tcp://192.168.31.169:61616
spring.activemq.user=admin
spring.activemq.password=admin
#如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
spring.jms.pub-sub-domain=false
1.生产者
@Component
public class Product {
@Resource
JmsTemplate jmsTemplate;
public void productSend(String msg){
ActiveMQQueue springbootQueue = new ActiveMQQueue("SpringbootQUEUE");
jmsTemplate.convertAndSend(springbootQueue,msg);
}
}
2.消费者
@Component
public class Consumer {
@JmsListener(destination = "SpringbootQUEUE")
public void consumers(TextMessage msg){
try {
System.out.println(msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
3.Controller测试
@RestController
@RequestMapping("test")
public class ProductController {
@Autowired
private Product product;
@RequestMapping("send")
public void send(String msg){
product.sendMessage(msg);
}
}
4.发送其他数据
生产者
创建对象
//注意:对象一定要实现Serializable接口
public class User implements Serializable {
private String name;
private String password;
private Integer age;
private Date bir;
}
#如果使用ObjectMessage传输对象,必须要加上这个信任包,否则会报ClassNotFound异常
spring.activemq.packages.trust-all: true
@JmsListener(destination = "UserTopic")
public void consumer(ObjectMessage msg){
try {
System.out.println("订阅者1-消费消息 "+msg.getObject());
} catch (JMSException e) {
e.printStackTrace();
}
}
10.2.发布订阅(pub& sub)
1.修改配置
#如果是点对点(queue),那么此处默认应该是false,如果发布订阅,那么一定设置为true
spring.jms.pub-sub-domain=true
2.生产者
@Component
public class Product {
@Resource
JmsTemplate jmsTemplate;
public void ProductSend(String msg){
ActiveMQTopic springbootTopic = new ActiveMQTopic("SpringbootTopic");
jmsTemplate.convertAndSend(springbootTopic,msg);
}
}
3.消费者1
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
try {
System.out.println(msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
4.消费者2
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
try {
System.out.println(msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
设置为true
spring.jms.pub-sub-domain=true
#### 2.生产者
~~~java
@Component
public class Product {
@Resource
JmsTemplate jmsTemplate;
public void ProductSend(String msg){
ActiveMQTopic springbootTopic = new ActiveMQTopic("SpringbootTopic");
jmsTemplate.convertAndSend(springbootTopic,msg);
}
}
3.消费者1
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
try {
System.out.println(msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
4.消费者2
@JmsListener( destination= "SpringbootTopic")
public void Consumers(TextMessage msg){
try {
System.out.println(msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}