一、入门概述
1. 前言
微服务架构后,链式调用是我们在写程序时候的一-般流程, 为了完成一个整体功能会将其拆先成多个函数(或子模块),比如模块A调用模块B,模块B调用模块C,模块C调用模块D。但在大型分布式应用中,系统间的RPC交 互繁杂,一个功能背后要调用上百个接口并非不可能,从单机架构过渡到分布式微服务架构的通例,这种架构会有哪些问题???
系统之间直接调用实际工程落地和存在的问题:
----a.系统间的接口耦合比较严重
----b.面对大流量时容易被冲垮
----c.等待同步存在性能问题
根据上述的几个问题,在设计系统时可以明确要达到的目标:
----①.要做到系统解耦, 当新的模块接进来时,可以做到代码改动最小;能够解耦
----②.设置流量缓冲池,可以让后端系统按照自身吞吐能力进行消费,不被冲垮:能够削峰
----③.强弱依赖梳理能将非关键调用链路的操作异步化并提升整体系统的吞吐能力:能够异步
2. 是什么
----a.定义:
面向消息的中间件(message-oriented middleware )MOM能够很好的解决以上问题。是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型在分布式环境下提供应用解耦、弹性伸缩、冗余存储、流量削峰、异步通信、数据同步等功能。
大致的过程是这样的:
发送者把消息发送给消息服务器,消息服务器将消息存放在若干队列/主题topic中,在合适的时候,消息服务器会将消息转发给接受者。在这个过程中,发送和接受是异步的,也就是发送无需等待,而且发送者和接受者的生命周期也没有必然关系;尤其在发布pub/订阅sub模式下,也可以完成一 -对多的通信,即让-一个消息有多个接受者。
----b.特点:
------①.采用异步处理模式
消息发送者可以发送一一个消息而无须等待响应。消息发送者将消息发送到一条虚拟的通道(主题或队列)上:
消息接收者则订阅或监听该通道。一条信 息可能最终转发给一一个或多个消息接收者,这些接收者都无需对消息发送者做出同步回应。整个过程都是异步的。
案例:
也就是说,一个系统跟另外一个系统之间进行通信的时候,假如系统A希望发送一-个消息给系统B,让他去处理。
但是系统A不关注系统B到底怎么处理或者有没有处理好,所以系统A把消息发送给MQ,然后就不管这条消息的“死活”了,接着系统.B从MQ里消费出来处理即可。至于怎么处理,是否处理完毕,什么时候处理,都是系统B的事儿,与系统A无关。
这样的一一种通信方式,就是所谓的“异步”通信方式对于系统A来说,只要把消息发给MQ,然后系统B就会异步的去进行处理了,系统A不需要“同步”的等待系统B处理完。这样的好处是什么呢?两个字:解耦
------②.应用系统间的解耦
发送者和接收者不必了解对方,只需要确认消息
发送者和接收者不必同时在线
------③.具体业务
3. 能干嘛
----a.解耦
----b.异步
----c.削峰
4. 去哪里下
ActiveMQ官网下载:http://activemq.apache.org/
5. 怎么玩
----a.最主要的功能----实现高可用,高性能,可伸缩,易用和安全的企业级面向消息服务的系统
----b.异步消息的消费和处理
----c.控制消息的消费顺序
----d.可以和spring与springboot 整合简化编码
----e.配置集群容错的mq集群
二、ActiveMQ的安装和控制台
- 官网下载
- Linux安装(需要提前准备好Java jdk 环境 因为activemq 需要它的支持)
----①.将下载到的压缩包apache-activemq-5.15.9-bin.tar.gz 放到Linux系统的/opt目录下面
----②.解压缩apache-activemq-5.15.9-bin.tar.gz
命令:tar -zxvf apache-activemq-5.15.9-bin.tar.gz
----③.在根目录下建myActivemq的目录(这个根据个人习惯)
命令:mkdir /myActiveMQ
----④.将解压的目录拷贝到myActiveMQ下
命令:cp -r apache-activemq-5.15.9 /myActiveMQ/
----⑤.普通启动
到刚刚复制的apache-activemq-5.15.9目录的bin下
执行命令:./activemq start
----⑥.重启activemq
命令:./activemq restart
----⑦.停止activemq
命令:./activemq stop
----⑧.带日志的启动方式(使用‘>
’ 命令方式指定将对activemq的所有操作记录到我们指定的日志文件中)
命令:./activemq start > /myactiveMQ/ run_actịvemq. log
- 访问Apache ActiveMQ控制台(前端页面)
在客户端(一般是win系统)浏览器输入:
http://你安装activemq那台电脑的IP地址:8161/admin/
口默认的用户名和密码是admin/admin
注意:要先保证作为客户端的win系统和Linux系统在同一个网段,即相互ping通
4. 备注
----①. activemq的默认进程端口号为61616
----②.查看activemq服务是否启动
命令:ps -ef|grep activemq
或ps -ef|grep activemq|grep -v grep (屏蔽掉含grep的进程服务)
或netstat -anp|grep 61616
或lsof -i:61616
----③.activemq 采用61616端口提供JMS服务
----④.activemq采用8161端口提供管理控制台服务
三、Java编码实现ActiveMQ通信
- IDEA建立maven工程
- POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.rocket</groupId>
<artifactId>test_activemq</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<!--activemq需要的jav包-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<!--下面是junit/log4等通用配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
3.JMS编码总体架构
对比回忆JDBC的编码套路
4. 目的地distination队列和主题的两大模式特性
5. 在点对点的消息传递域中,目的地被称为队列(queue)
上手案例:
----①.消息生产者:
public static final string ACTIVEMO_URL = "tcp://192.168.120.130:61616";
public static final String QUEUE_NAME = "queuetest" ;
public static void main(String[] args) throws JMSException {
//1 创建连接工厂,按照给定的url地址,采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//2 通过连接工厂,获得连接Connection并启动访问
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//3 创建会话Session 两个参数,第一个叫事务,第二个叫签收
Session session = connection.createSession(false, Session. AUTO_ACKNOWLEDGE);
//4 创建目的地(具体是队列queue 还是主题topic)这里是队列
Queue queue = session.createQueue(QUEUE_NAME);
//5 创建消息生产者
MessageProducer messageProducer = session.createProducer(queue);
//6通过messageProducer生6条消息发送到MQ的队列里面
for(int i=1;i<=6;i++){
//7 创建消息
TextMessage textMessage = session. createTextMessage("ms---"+ i);//理解为一个字符串
//8 通过messageproducer发送mq
messageProducer.send(textMessage);
}
//9关闭资源
messageProducer.close();
session.close();
connection.close();|
System.out.println("******消息发布到MQ完成" );
}
控制台查看:
控制台说明:
总结:
当有一个消息进入这个队列时,等待消费的消息是1,进入队列的消息是1。
当消息消费后,等待消费的消息是0,进入队列的消息是1,出队列的消息是1.
再来一条消息时,等待消费的消息是1,进入队列的消息就是2.
----②.消息消费者
public static final string ACTIVEMO_URL = "tcp://192.168.120.130:61616";
public static final String QUEUE_NAME = "queuetest" ;
public static void main(String[] args) throws JMSException {
//1 创建连接工厂,按照给定的url地址,采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//2 通过连接工厂,获得连接Connection并启动访问
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//3 创建会话Session 两个参数,第一个叫事务,第二个叫签收
Session session = connection.createSession(false, Session. AUTO_ACKNOWLEDGE);
//4 创建目的地(具体是队列queue 还是主题topic)这里是队列
Queue queue = session.createQueue(QUEUE_NAME);
//5 创建消息消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
/*同步阻塞方式(receive())
订阅者或接收者湖Messageconsumer的receive()方法来接收消息,receive 方法在能够接收到游息之前(或超时之前)將一直阻塞。*/
/*
while (true){
//生产者是TextMessage 类型所以消费者也要是TextMessage
//receive()接收方法不带参数表示一直等待 如果带参数long类型参数 表示等待几毫秒后不等待
TextMessage textMessage = (TextMessage)messageConsumer.receive();
if(null I= textMessage){
System.out.println("***消费者接收到消息: "+textMessage.getText());
}else {
break;
}
}
messageConsumer.close();
session.close();
connection. close();
*/
/*通过监听器的方式来消费消息
异步非阻塞方式(监听器onMassage())
订阅者或接收者通过MessageConsumer的setMessageListener(MessageListener listener)注册一个消息监听器,当消息到达之后,系统自动调用监听器MessageListener的onMessage(Message message)方法
*/
messageConsumer.setMessageListener(new MessageListener()
{
@Override
public void onMessage(Message message)
{
if(null != message && message instanceof TextMessage)
{
TextMessage textMessage = (TextMessage) message ;
try
{
System.out.println("**消费者接收到的消息: "+textMessage.getText());
}catch (JMSException e) {
e.printstackTrace();
}
}
});
System.in.read();//必须写 这个保证程序一直运行 控制台不灭
// 关闭资源
messageconsumer.close();
session.close(); :
connection.close();
}
案例假设和结论:
----③.编码小总结:
--------a.JMS开发的基本步骤
--------b.两种消费方法
--------c.点对点消息传递的特点:
6. 在发布订阅消息传递域中,目的地被称为主题(topic)
----①.发布主题生产者
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic01";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
MessageProducer producer = session.createProducer(topic);
for (int i = 1; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("Topic mag..." + i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("完成....");
}
----②.订阅主题消费者
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic01";
public static void main(String[] args) throws JMSException, IOException, InterruptedException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
MessageConsumer consumer = session.createConsumer(topic);
// while (true){
// TextMessage receive = (TextMessage) consumer.receive();
// if (null != receive){
// System.out.println("消息:"+receive.getText());
// }
// else {
// System.out.println("结束");
// break;
// }
// }
/*consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if (null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("消费者接收到消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});*/
//Lambda表达式写法
consumer.setMessageListener((message) -> {
if (null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("消费者接收到消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
System.in.read();
consumer.close();
session.close();
connection.close();
System.out.println("完成....");
}
----③.编码小总结:
--------a.先启动消费者后启动生产者,不然发送的消息是废消息
--------b.发布订阅消息传递域的特点:
7. 两大模式比较
四、JMS规范和落地产品
1. 是什么
----①.javee
----②.jms
java Message Service(java 消息服务是javaee中的一个技术)
2. MQ中间件的其他落地产品
----①落地产品
----②.产品详细对比
3. JMS的组成和特点
----①.JMS privider–实现JMS接口与规范的消息中间件,也就是我们的MQ服务器
----②.JMS producer–消息生产者,创建与发送JMS消息的客户端应用
----③.JMS consumer–消息的消费者,接受与处理JMS消息的客户端应用
----④.JMS message
--------a.消息头(以下是比较常用)
-----------1.JMSdesination
---------- 消息发送的目的地,主要是指Queue与Topic
-----------2.JMSDeliveryMode
-----------3.JMSExpiration
-----------4.JMSPriority
-----------5.JMSMessageID
-----------唯一识别每个消息的表示,由MQ产生
--------b.消息体–封装具体消息的数据(发送和接受的消息体类型必须是一致对应的)
五种消息体的格式:
-----------1.TextMessage(常用)—普通的字符串消息,包含一个string
-----------2.MapMessage(常用)–一个map类型的消息,key为stirng类型,而value 为java的基本类型
-----------3.BytesMessage–二进制数组消息,包含一个byte[]
-----------4.StreamMessage–java数据流消息,用标准流操作来顺序的填充或读取
-----------5.ObjectMessage–对象消息,包含一个可序列化的java对象
----c.消息属性
--------定义:
-------作用:如果需要使用消息头以外的值,那么可以使用消息属性(一种加强型的api),识别/去重/重点标注等操作非常有用的方法
4. JMS的可靠性
----①.persistent:持久性
--------a.参数设置说明:
-----------1.持久化(默认):当服务器宕机,消息依然存在。
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
-----------2.非持久化:当服务器宕机,消息不存在。
messageProducersetDeliveryMode(DeliveryMode.NON_PERSISTENT);
--------b.持久的queue
这是队列的的默认传送模式,此模式保证这些消息只被传送r次和成功使用一次。对于这些消息,可靠性是优先考虑的因素。
可靠性的另一个重要方面是确保持久性消息传送至目标后,消息服务在向消费者传送它们之前不会丢失这些消息。
--------c.持久的topic
--------①.先启动订阅在启动生产
--------②.持久的发布主题生产者 的代码
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic_p";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
MessageProducer producer = session.createProducer(topic);
//设置主题生产者使用持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//启动访问
connection.start();
for (int i = 1; i <=3; i++) {
TextMessage textMessage = session.createTextMessage("Topic mag..." + i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("完成....");
}
--------③.持久的订阅主题消费者 的代码
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic_p";
public static void main(String[] args) throws JMSException, IOException, InterruptedException {
System.out.println("我是3号消费者。。。");
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
//设置订阅者id
connection.setClientID("z3");
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
//设置持久的订阅者
TopicSubscriber durableSubscriber = session.createDurableSubscriber(topic, "remark..");
//启动访问
connection.start();
Message message = durableSubscriber.receive();
while (null!=message){
TextMessage textMessage = (TextMessage)message;
System.out.println("收到的持久化:"+textMessage.getText());
message = durableSubscriber.receive(1000L);
}
System.in.read();
session.close();
connection.close();
System.out.println("完成....");
}
注意:(类似微信公众号的订阅发布)
--------1.一定要先运行次消费者,等F向MQ注册, 类似我订阅了这个主题。
--------2.然后内运行生产者发送信息,此时,
--------3.无论消费者是否在线,都会接收到,不在线的话,下次连按的时候,会把没有收过的消目都按收来。
--------d.备注:
非持久的topic 没有意义,因为发布订阅模式是先启动订阅在启动生成,消息已经被消费了。如果先启动生产者后启动订阅者,消息会被当作废消息
----②.transaction:事务(偏生产者)
---------a.事务偏生产者/签收偏消费者
---------b.producer提交时的事务
------------①.设置为 false时:
//创建会话Session 两个参数,第一个叫事务,第二个叫签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
------------只要执行send就进入到队列中
------------关闭事务,那第2个签收参数的设置需要有效
------------②.设置为 true时:
//创建会话Session 两个参数,第一个叫事务,第二个叫签收
Session session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
session.commit();
------------先执行send再执行commit,消息才被真正的提交到队列中(关闭之前需要commit,这是在生产者的事务,负责消息的提交不出错,如果出错则回滚)
------------消息需要批量发送,需要缓存区处理
----③.Acknowledge:签收(偏消费者)
----------a.非事务(不启动事务情况下)
--------------1.自动签收(默认)
Session.AUTO_ACKNOWLEDGE
--------------2.手动签收
Session.CLIENT_ACKNOWLEDGE
//客户端(消费者)调用acknowledge方法手动签收
message.acknowledge();
--------------3.允许重复消息
Session.DUPS_OK_ACKNOWLEDGE
----------b.事务(启动事务情况下)
----------消费者事务开启,只有commit后才能将全部消息变为已消费,写ack没有作用,即使写了签收也不会签收出队
----------c.签收与事务的关系
-------------1.在事务性会话中,当一个事务被成功提交则消息被自动签收。如果事务回滚,则消息会被再次传送
-------------2.非事务会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement)
5. JMS的点对点模式总结
点对点模型是基于队列的,生产者发消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。和我们平时给朋友发送短信类似。
1:如果在Session关闭时有部分消息已被收到但还没有被签收(acknowledged),那当消费者下次连接到相同的队列时,这些消息还会被再次接收
2:队列可以长久地保存消息直到消费者收到消息。消费者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势
6. JMS的发布订阅模式总结
JMSPub/Sub模型定义了如何向一个内容节点发布和订阅消息,这些节点被称作topic
主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息。主题使得消息订阅者和消息发布者保持互相独,不需要接触即可保证消息的传送。
-------a.非持久订阅
非持久订阅只有当客户端处于激活状态,也就是和MQ保持连接状态才能收到发送到某个主题的消息。
如果消费者处于离线状态,生产者发送的主题消息将会丢失作废,消费者永远不会收到。
一句话:先要订阅注册才能接受到发布,只给订阅者发布消息。
-------b.持久订阅
客户端首先向MQ注册一个自 己的身份ID识别号,当这个客户端处于离线时,生产者会为这个ID保存所有发送到主题的消息,当客户再次连接到MQ时会根据消费者的ID得到所有当自己处于离线时发送到主题的消息。
非持久订阅状态下,不能恢复或重新派送一个未签收的消息。
持久订阅才能恢复或重新派送一个未签收的消息。
-------c.用哪个?
当所有消息必须被接受,则用持久订阅。当丢失消息能够被容忍,则用非持久订阅
五、ActiveMQ的Broker
- 是什么
相当于一个ActiveMQ服务器实例
说白了,Broker其实 就是实现了用代码的形式启动ActiveMQ将MQ嵌入到Java代码中,以便随时用随时启动,在用的时候再去启动这样能节省了资源,也保证了可靠性。
不同的conf配置文件模拟不同的实例
- 嵌入式的Broker
用ActiveMQ Broker 作为独立的消息服务器来构建JAVA应用。
ActiveMQ也支持在vm中通信基于嵌入式的broker,能够在无缝的集成其他java应用。
六、Spring整合ActiveMQ(重)
- maven修改,需要添加spring支持的jms的包
pom.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<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.rocket</groupId>
<artifactId>test_activemq</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<!--activemq需要的jav包-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<!--下面是junit/log4等通用配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
- 2.spring配置文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启spring的包自动扫描 -->
<context:component-scan base-package="com.test.activemq"></context:component-scan>
<!--配置生产者 -->
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<!--真正可以生产connection的ConnectionFactory,由对应的JMS服务厂商提供 -->
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://10.112.70.211:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"></property>
</bean>
<!--这个是队列目的地,点对点 -->
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<!-- Spring的JMS模版工具 , 它可以进行消息发送、接收等-->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
<property name="defaultDestination" ref="destinationQueue"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter" />
</property>
</bean>
</beans>
- 队列
------a.生产者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
@Service
public class SpringMQ_Producer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Producer producer = (SpringMQ_Producer) applicationContext.getBean("springMQ_Producer");
producer.jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage("Spring+Active。。。。");
return textMessage;
}
});
}
}
------b.消费者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
@Service
public class SpringMQ_Consumer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) throws JMSException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Consumer consumer = (SpringMQ_Consumer) applicationContext.getBean("springMQ_Consumer");
// TextMessage receive =(TextMessage) consumer.jmsTemplate.receive();
String receive =(String) consumer.jmsTemplate.receiveAndConvert();
System.out.println("消费者:"+receive);
}
}
- 主题
-------a.Spring配置文件,新增主题topic
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.activemq"></context:component-scan>
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://10.112.70.211:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"></property>
</bean>
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
</bean>
<!-- Spring的JMS模版工具 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
<property name="defaultDestination" ref="destinationTopic"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter" />
</property>
</bean>
</beans>
-------b.生产者:同队列代码不变
-------c.消费者:同队列代码不变
5. 在spring中里边实现消费者不启动,直接通过配置监听完成(只需要启动生产者,不需要启动消费者,自动完成监听记录)
-------a.spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu.activemq"></context:component-scan>
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://10.112.70.211:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"></property>
</bean>
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
</bean>
<!-- Spring的JMS模版工具 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory" />
<property name="defaultDestination" ref="destinationTopic"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter" />
</property>
</bean>
<!--配置监听程序 -->
<bean id="queueListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory" />
<property name="destination" ref="destinationTopic" />
<!--public class MyMessageListener implements MessageListener -->
<property name="messageListener" ref="myMessageListener" />
</bean>
</beans>
-------b.需要写一个类来实现监听
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Service
public class MyMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if (null != message && message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("spring消费者接收到消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
七、SpringBoot整合ActiveMQ(重)
- 队列生产者
------a.新建maven工程并设置包名类名
------b.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<groupId>com.rocket</groupId>
<artifactId>springboot_activemq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_activemq</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
------c.application.yum
server:
port: 7777
spring:
activemq:
broker-url: tcp://10.112.70.211:61616 # 自己的MQ服务器地址
user: admin
password: admin
jms:
pub-sub-domain: false # false=Queue true= Topic 默认是false
# 自己定义的队列的名称
myqueue: boot-activemq-queue
------d.配置bean(类似Spring框架的applicationContext.xml文件)
package com.rocket.springboot_activemq.config;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
@Component
@EnableJms
public class ConfigBean {
@Value("${myqueue}")
private String myQueue;
@Bean
public Queue queue(){
return new ActiveMQQueue(myQueue);
}
}
------e.队列生产者类(Queue_Produce)
package com.rocket.springboot_activemq.produce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.UUID;
@Component
public class Queue_Produce {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
public void produceMsg(){
jmsMessagingTemplate.convertAndSend(queue,"******:"+ UUID.randomUUID().toString().substring(0,6));
}
}
------f.主启动类MainApp_Produce
------g.测试单元
-----新需求:要求每隔3秒往MQ推送消息,
以下定时发送case,案例修改
------a.修改Queue_Produce新增定时投递方法
@Scheduled(fixedDelay = 3000)
public void produceMsgScheduled(){
jmsMessagingTemplate.convertAndSend(queue,"******Scheduled:"+ UUID.randomUUID().toString().substring(0,6));
System.out.println("发送消息。。。");
}
------b.修改主启动类增加@EnableScheduling注解,然后直接开始主启动类,间隔发消息OK
2. 队列消费者
-------a.新建maven工程并设置包名类名
-------b.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rocket</groupId>
<artifactId>boot_mq_consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot_mq_consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-------c.application.yml
server:
port: 8888
spring:
activemq:
broker-url: tcp://10.112.70.211:61616
user: admin
password: admin
jms:
pub-sub-domain: false
myqueue: boot-activemq-queue
-------d…队列消费者类(Queue_Consumer)
package com.rocket.boot_mq_consumer.consumer;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.TextMessage;
@Service
public class QueueConsumer {
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage) throws JMSException {
System.out.println("消费者接受消息:"+textMessage.getText());
}
}
-------e.主启动类
package com.rocket.boot_mq_consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootMqConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootMqConsumerApplication.class, args);
}
}
- Topic生产者
-------a.新建maven工程并设置包名类名
-------b.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rocket</groupId>
<artifactId>boot_mq_topic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot_mq_topic_consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-------c.application.yum
server:
port: 7777
spring:
activemq:
broker-url: tcp://10.112.70.211:61616
user: admin
password: admin
jms:
pub-sub-domain: true # true值表示topic
mytopic: boot-activemq-topic
-------d.配置bean
package com.rocket.boot_mq_topic.config;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import javax.jms.Topic;
@Component
@EnableJms
public class ConfigBean {
@Value("${mytopic}")
private String myTopic;
@Bean
public Topic queue(){
return new ActiveMQTopic(myTopic);
}
}
-------e.主题生产者类(Topic_Produce)
package com.rocket.boot_mq_topic.produce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import javax.jms.Topic;
import java.util.UUID;
@Component
public class Topic_Produce {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic topic;
public void produceMsg(){
jmsMessagingTemplate.convertAndSend(topic,"Topic******:"+ UUID.randomUUID().toString().substring(0,6));
}
@Scheduled(fixedDelay = 3000)
public void produceMsgScheduled(){
jmsMessagingTemplate.convertAndSend(topic,"Topic******Scheduled:"+ UUID.randomUUID().toString().substring(0,6));
System.out.println("发送消息。。。");
}
}
-------f.主启动类MainApp_Produce
package com.rocket.boot_mq_topic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class BootMqTopicApplication {
public static void main(String[] args) {
SpringApplication.run(BootMqTopicApplication.class, args);
}
}
- Topic消费者
-------a.新建maven工程并设置包名类名
-------b.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rocket</groupId>
<artifactId>boot_mq_topic_consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot_mq_topic_consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.targer>1.8</maven.compiler.targer>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-------c.application.yml
server:
port: 5555
spring:
activemq:
broker-url: tcp://10.112.70.211:61616
user: admin
password: admin
jms:
pub-sub-domain: true
myqueue: boot-activemq-topic
-------d.主题消费者类()Topic_Consumer
package com.rocket.boot_mq_topic_consumer.consumer;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.TextMessage;
@Service
public class TopicConsumer {
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage) throws JMSException {
System.out.println("消费者接受消息:"+textMessage.getText());
}
}
-------e.主启动类
package com.rocket.boot_mq_consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootMqConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(BootMqConsumerApplication.class, args);
}
}
八、ActiveMQ传输协议(重)
-
官网
http://activemq.apache.org/configuring-version-5-transports.html -
是什么
-
有哪些
-------a.TCP默认的
-------b.NIO
-------c.AMQP协议
-------d.stomp协议
-------e.SSL
-------f.mqtt协议
-------g.ws协议
此传输使用新的HTML5 WebSocket与代理交换消息。
-------h.小总结
-
NIO案例演示
--------a.官网:http://activemq.apache.org/configuring-transports
--------b.修改配置文件activemq.xml
-------c.生产者和消费者两端协议修改:
public static final String URL = "nio://10.112.70.211:61616";
public static final String queue_name = "Transport";
- NIO案例演示增强
-------a.问题:
UPI格式头以“nio”开头,表示这个端口使用以TCP为基础的NIO网络IO模型
但是这样的设置方式,只能使这个端口支持openwire协议。那么我们怎么即让这个端口支持NIO网络io模型,又让它支持多个协议呢?
-------b.解决:
使用auto关键字 和 使用“+”符号来为端口设置多种特性
九、ActiveMQ的消息存储与持久化(重)
-------a.官网
http://activemq.apache.org/persistence
-------b.是什么
-------c.有哪些
---------①.ActiveMQ Message Store (了解)
** --------------基于文件的存储方式,是以前的默认消息存储,现在不用了**
---------②.KahaDB 消息存储(默认)
---------------基于日志文件,从ActiveMQ5.4开始默认的持久化插件
---------------查看activeMQ存储机制方式:
---------------kahadb说明:
---------------kahadb存储原理:
---------③.LevelDB消息存储(了解)
---------④.JDBC 消息存储(重点)
---------⑤.JDBC Message store with ActiveMQ Journal(重点)
-------d.JDBC消息存储(重点)
-------------1.MQ+Mysql
-------------2.添加mysql数据库的驱动包到lib文件夹
-------------3.jdbcPersistanceAdapter配置
-------------4.数据库连接池配置
-------------5.建库建表说明
----------------a.建一个名为activemq的数据库:create database activemq;
----------------b.三张表的说明
-------------------①.activemq_msgs表
-------------------②.activemq_acks表
-------------------③.activemq_lock表
----------------c.如果新建数据库OK+上述配置OK+代码运行OK,三张表会自动建立
----------------d.万一情况,手动建表sql如果配置不好需要手动,应急
可以百度
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for ACTIVEMQ_ACKS
-- ----------------------------
DROP TABLE IF EXISTS `ACTIVEMQ_ACKS`;
CREATE TABLE `ACTIVEMQ_ACKS` (
`CONTAINER` varchar(250) NOT NULL,
`SUB_DEST` varchar(250) DEFAULT NULL,
`CLIENT_ID` varchar(250) NOT NULL,
`SUB_NAME` varchar(250) NOT NULL,
`SELECTOR` varchar(250) DEFAULT NULL,
`LAST_ACKED_ID` bigint(20) DEFAULT NULL,
`PRIORITY` bigint(20) NOT NULL DEFAULT '5',
`XID` varchar(250) DEFAULT NULL,
PRIMARY KEY (`CONTAINER`,`CLIENT_ID`,`SUB_NAME`,`PRIORITY`),
KEY `ACTIVEMQ_ACKS_XIDX` (`XID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for ACTIVEMQ_LOCK
-- ----------------------------
DROP TABLE IF EXISTS `ACTIVEMQ_LOCK`;
CREATE TABLE `ACTIVEMQ_LOCK` (
`ID` bigint(20) NOT NULL,
`TIME` bigint(20) DEFAULT NULL,
`BROKER_NAME` varchar(250) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for ACTIVEMQ_MSGS
-- ----------------------------
DROP TABLE IF EXISTS `ACTIVEMQ_MSGS`;
CREATE TABLE `ACTIVEMQ_MSGS` (
`ID` bigint(20) NOT NULL,
`CONTAINER` varchar(250) DEFAULT NULL,
`MSGID_PROD` varchar(250) DEFAULT NULL,
`MSGID_SEQ` bigint(20) DEFAULT NULL,
`EXPIRATION` bigint(20) DEFAULT NULL,
`MSG` longblob,
`PRIORITY` bigint(20) DEFAULT NULL,
`XID` varchar(250) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `ACTIVEMQ_MSGS_MIDX` (`MSGID_PROD`,`MSGID_SEQ`),
KEY `ACTIVEMQ_MSGS_CIDX` (`CONTAINER`),
KEY `ACTIVEMQ_MSGS_EIDX` (`EXPIRATION`),
KEY `ACTIVEMQ_MSGS_PIDX` (`PRIORITY`),
KEY `ACTIVEMQ_MSGS_XIDX` (`XID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-------------6.代码运行验证
----------------①.一定要开启持久化:producer.setDeliveryMode(DeliveryMode.PERSISTENT);
----------------②.队列queue
--------------------生产者:
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce {
public static final String URL = "tcp://10.112.70.211:61616";
public static final String queue_name = "topic_p_mysql";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(queue_name);
MessageProducer producer = session.createProducer(queue);
//开启持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
connection.start();
for (int i = 1; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("mag2..." + i);
producer.send(textMessage);
}
producer.close();
// session.commit();
session.close();
connection.close();
System.out.println("....");
}
}
--------------------消费者:
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer {
public static final String URL = "tcp://10.112.70.211:61616";
public static final String queue_name = "topic_p_mysql";
public static void main(String[] args) throws JMSException, IOException, InterruptedException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.DUPS_OK_ACKNOWLEDGE);
Queue queue = session.createQueue(queue_name);
MessageConsumer consumer = session.createConsumer(queue);
while (true){
TextMessage receive = (TextMessage) consumer.receive(1000);
if (null != receive){
System.out.println("消息:"+receive.getText());
}
else {
System.out.println("结束");
break;
}
}
// System.in.read();
// Thread.sleep(1000);
consumer.close();
// session.commit();
session.close();
connection.close();
System.out.println("完成....");
}
}
----------------③.主题topic
--------------------生产者:
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce_Topic_p {
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic_p_mysql";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
MessageProducer producer = session.createProducer(topic);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
connection.start();
for (int i = 4; i <=6; i++) {
TextMessage textMessage = session.createTextMessage("Topic mag..." + i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("完成....");
}
}
--------------------消费者:
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.camel.component.test.TestComponent;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer_Topic_p {
public static final String URL = "tcp://10.112.70.211:61616";
public static final String topic_name = "topic_p_mysql";
public static void main(String[] args) throws JMSException, IOException, InterruptedException {
System.out.println("我是3号消费者。。。");
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
Connection connection = factory.createConnection();
connection.setClientID("z3");
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(topic_name);
TopicSubscriber durableSubscriber = session.createDurableSubscriber(topic, "remark..");
connection.start();
Message message = durableSubscriber.receive();
while (null!=message){
TextMessage textMessage = (TextMessage)message;
System.out.println("收到的持久化:"+textMessage.getText());
message = durableSubscriber.receive(1000L);
}
// Thread.sleep(1000);
session.close();
connection.close();
System.out.println("完成....");
}
}
-------------7.数据库情况
-----------------①.点对点类型(队列)
-----------------②.一对多类型(主题)
-------------8.小总结
-------------9.开发有坑
-------e.JDBC Message store with ActiveMQ Journal(重点)
-------------①.是什么
-------------②.配置
-------------③.重启配置文件激活
-------------④.运行
-------f.ActiveMQ持久化机制小总结
十、ActiveMQ多节点集群(重)
---------1.是什么
基于zookeeper和leveldb搭建activemq集群集群仅提供主备方式的高可用集群功能,避免单点故障
官网:http://activemq.apache.org/replicated-leveldb-store
---------2.zookeeper+replicated-leveldb-store的主从集群
(1)zookeeper与activemq原理
使用ZooKeeper实现的Master-Slave实现方式,是对ActiveMQ进行高可用的一种有效的解决方案,高可用的原理:使用ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker可以对外提供服务(也就是Master节点),其他的Broker处于待机状态,被视为Slave。如果Master因故障而不能提供服务,则利用ZooKeeper的内部选举机制会从Slave中选举出一个Broker充当Master节点,继续对外提供服务。
四、高可用+负载均衡实现
Broker-Cluster 可以解实现载均衡,但当其中一个 Broker 突然宕掉的话,那么存在于该 Broker 上处于 Pending 状态的 message 将会丢失,无法达到高可用的目的。Master-Slave 与 Broker-Cluster 相结合的部署
原文:https://blog.youkuaiyun.com/zyjavaWeb/article/details/79209572
------------①.三种集群方式的对比:基于共享文件系统kahadb默认;基于JDBC;基于可复制的LevelDB
官网:http://activemq.apache.org/masterslave.html
------------②.本次案例采用zk+levelDB
---------------部署规划和步骤:
------------------1.环境和版本
------------------2.关闭防火墙并保证win可以ping通过activemq服务器
------------------3.要求具备zookeeper集群并可以成功启动
------------------4.集群部署规划列表
------------------5.创建3台集群目录
最终效果:
------------------6.修改管理控制台端口
mq_node02:
mq_node03:
------------------7.hostname名字映射
------------------8.activemq集群配置
1.路径:
2.brokerName:
3.持久化:在每个conf/activemq.xml中
------------------9.修改各个节点的消息端口
mq_node02:
mq_node03:
------------------10.按照顺序启动3个ActiveMQ节点,到这步前题是zk集群已经成功启动运行
这里为了方便编写脚本批处理执行zk,脚本内容为:
这里为了方便编写脚本批处理执行mq,脚本内容为:
------------------11.zk集群的节点状态说明
1.3台zk集群链接任意一台:
2.查看Master
------------③.集群可用性测试
1.干掉:
2.代码:
十一、高级特性
-------------1.异步投递Async Sends
对于一个slow consumer ,使用同步发送消息可能出现producer堵塞等情况,慢消费者适合使用异步发送
-----------------①.官网:http://activemq.apache.org/async-sends
-----------------②.是什么?
ActiveMQ支持同步、异步两种发送的模式将消息发送到broker,模式的选择对发送延时有巨大的影响。producer能达到怎样的产出率(产出率=发送数据总量/时间)主要受发送延时的影响,使用异步发送可以显著的提高发送的性能。
**ActiveMQ默认使用异步发送的模式:**除非明确指定使用同步发送的方式或者在未使用事务的前提下发送持久化的消息,这两种情况都是同步发送的。
如果你没有使用事务且发送的是持久化的消息,每- -次发送都是同步发送的且会阻塞producer直到broker返回-一个确认,表示消息已经被安全的持久化到磁盘。确认机制提供了消息安全的保障,但同时会阻塞客户端带来了很大的延时。
很多高性能的应用,允许在失败的情况下有少量的数据丢失。如果你的应用满足这个特点,你可以使用异步发送来提高生产率,即使发送的是持久化的消息。
异步发送
它可以最大化produer端的发送效率。我们通常在发送消息量比较密集的情况下使用异步发送,它可以很大的提升Producer性能;不过这也带来了额外的问题,
就是需要消耗较多的Client端内存同时也会导致broker端性能消耗增加;
此外它不能有效的确保消息的发送成功。在useAsyncSend=true的情况下客户端需要容忍消息丢失的可能。
-----------------③.官网配置方式
.---------------④…异步发送如何确认发送成功?
-------------2.延时投递和定时投递
-------------3.Activemq消费重试机制
-------------4.死信队列
-------------5.如何保证消息不被重复消费呢?幂等性问题?
-------------6.引入消息队列之后该如何保证其高可用性