MessageQueue 消息队列,也称消息中间件。对于模块化系统之间复杂调用,使用MQ形式传递调用过程,能解耦;避免支线业务造成阻塞,使用MQ的消息回调,能异步;瞬间访问,业务堆积,使用MQ延迟处理边缘业务,能削峰。本文(一到四)段简要介绍MQ,后(五到九)为核心。
一、简单入门
本文选择ActiveMQ作为消息队列的产品实现。MQ产品很多,但他们都遵从JMS的消息规范。安装好ActiveMQ即可进行编码尝试。
1、队列消息生产者编码
public class JmsProdece {
public static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";//ip+端口的形式,选择Linux版本的更佳
public static final String QUEUE_NAME = "myQueue01";
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、创建目的地(具体是主题还是队列) Destination是接口 Queue和Topic是实现
Queue queue = session.createQueue(QUEUE_NAME);
//5、创建消息的生产者
MessageProducer messageProducer = session.createProducer(queue);
//6、 发送消息 消息生产者生产三条消息,发送到mq的队列里面
for (int i = 1;i <=10 ; i++) {
//7、创建消息
//字符串
TextMessage textMessage = session.createTextMessage("myMsg---" + i);
//8、通过消息生产者发布 通过messageProducer 发送给MQ
messageProducer.send(textMessage);
}
//9、关闭资源
messageProducer.close();
session.close();
connection.close();
System.out.println("************消息生产并且成功发送到MQ************");
}
}
2、队列消息消费者编码
public class JmsConsumer {
public static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616";
public static final String QUEUE_NAME = "myQueue01";
public static void main(String[] args) throws JMSException, IOException {
//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、创建目的地(具体是主题还是队列) Destination是接口 Queue和Topic是实现
Queue queue = session.createQueue(QUEUE_NAME);
//上边跟消息生产者是一样的
/***************************************************************/
//5、创建消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
/**
* receive()
* 同步阻塞方式
* 订阅者或接受者调用MessageConsumer的receive()方法来接收消息,
* receive方法在能够接收到消息之前(或超时之前)将一直阻塞
*/
/*
while(true){
//等一段时间,3秒
//messageConsumer.receive(3000);
//一直等,死等
TextMessage textMessage =(TextMessage) messageConsumer.receive();
if(textMessage != null){
System.out.println("消费者受到消息为:"+textMessage.getText());
}else {
break;
}
}
messageConsumer.close();
session.close();
connection.close();*/
/**
* 异步非阻塞
* 通过监听器的方式来消费消息,MessageConsumer messageConsumer= session.createConsumer(queue);
*/
messageConsumer.setMessageListener(new MessageListener(){
@Override
public void onMessage(Message message) {
if(message != null && 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();
}
}
3、测试
对于Queue,先启动生产者,再启动消费者。8161是ActiveMQ的默认前台管理端口,61616是默认的后台服务连接端口。
生产者启动
消费者启动
二、JMS规范
1、概念
JMS是JavaEE的13个核心规范工业标准之一。
Java Message Service 也即Java消息服务,属于JavaEE范畴。是两个应用程序之间进行异步通讯的API,他为标准消息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持Java应用程序的开发。
在JMS规范中,一个MQ应该有四个组成部分。
- JMS Provider 服务器,实现JMS接口和规范的消息中间件。
- JMS Produce 生产者,创建和发送JMS消息的客户端应用。
- JMS Consumer 消费者,接收和处理JMS消息的客户端应用。
- Message 消息本身(下文会详细讲述)。
2、产品
3、消息
JMS消息的一些重要特征属性。
1、JMS Destination 消息发送的目的地。
主要是Queue和Topic。Queue即消息队列(一对一),Topic即主题(一对多)。
2、JMS DeliveryMode 持久模式和非持久模式
一条持久性消息:应该被传送一次并且仅仅一次,这就意味着如果JMS提供者出现故障, 该消息不会丢失,它会在服务器恢复之后再次传递。
一条非持久的消息,最多会传送一次,这意味着服务器出现故障,该消息将会永远丢失。
3、JMS Expriration 消息的过期时间
设置消息在一定时间之后过期,默认是永不过期。消息过期时间,等于Sestination的send方法中的timeToLive值加上发送时刻的GMT时间值。如果timeToLive设置为0,则JMSExpiration被设置为0,表示消息永不过期。如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被清除。
4、JMS Priority 优先级
消息的优先级,从0-9共10个级别,0-4是普通消息,5-9是加急消息。JMS不要求MQ严格按照这10个优先级发送消息,但是必须保证加急消息要先于普通消息到达。默认是4级。
5、JMS MessageID
唯一识别每一个消息的标识,由MQ自己产生。
5、消息体
JMS支持5种消息体格式,封装具体的消息数据,发送和接受的消息体类型必须一一对应。
TextMessage 普通的字符串消息,包含一个String
MapMessage 一个Map类型,Key是String,value是基本类型
BytesMessage 二进制数组,一个byte[]
StreamMessage Java数据流消息,用标准流操作来顺序填充和读取
ObjectMessage 对象消息,一个可序列化的Java对象
4、JMS的可靠性保证
持久 Persistent
非持久:当服务器宕机,消息不存在。massageProducer.setDeliverMode(DeliveryMode.NON_PERSISTENT);
持久化:服务器宕机,消息依然存在。massageProducer.setDeliverMode(DeliveryMode.PERSISTENT);
队列默认是持久化的。
持久的Topic 类似于微信公众号
1、首先一定要先运行一次消费者,等于向MQ注册,类似于我订阅了这个主题。
2、然后再运行生产者发送消息。
3、消费者是否在线,都会接收到消息。不在线当再次连接的时候,还是会收到没有接收过的消息
事务 Transaction
connection创建session的时候,填写参数。
false:只要执行send,就会进入到队列中。关闭事务,那第2个签收参数的设置需要有效。
true:先执行send再执行commit,消息才被真正的提交到队列中。消息需要批量发送,需要缓冲区处理。
消息生产和消费时候的事务都很重要。消费的时候事务不提交,会导致消息重复消费。
签收 Acknowledge
自动签收(默认)AUTO_ACKNOWLEDGE
手动签收CLIENT_ACKNOWLEDGE 客户端调用acknowledge方法手动签收 不手动签收会重复收到
允许重复消息 DUPS_OK_ACKNOWLEDGE
如果开启了事务,要写commit。如果是手动签收,要写ack。
生产事务开启,只有commit之后才能将全部消息变成已消费。
签收和事务的关系
在事务性回话中,当一个事务被成功提交则消息被自动签收。如果事务回滚,则消息会被再次传送。
非事务性会话中,消息如果被确定取决于会话创建时的应答模式 acknoledgement mode
下面两节是关于ActiveMQ如何在实际工作中配合Spring\SpringBoot两大框架进行开发,其实在GitHub上有很多的案例,只需要在官网上搜索即可。下文简单列出个重要步骤的截图。
三、Spring整合ActiveMQ
只展示Queue类型
配置bean
生产者编码
消费者编码
四、SpringBoot整合ActiveMQ
1、核心pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>
<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.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、yml配置
3、Config配置Bean
4、生产者
5、测试用例
6、结果
消费者也是一个SpringBoot模块,代码大致类似。
5、ActiveMQ传输协议(重点)
ActiveMQ支持的client-broker通讯协议有:TCP,NIO,UDP,SSL,Http(s)。其中配置Transport Connection 的文件在activemq安装目录的conf/activemq.xml文件的标签体内。
总共有如下传输协议
1、Transmission Control Protocol TCP 默认
2、New I/O Protocol NIO
3、AMQP
4、stomp
5、Secure Socket Layer Protocol SSL
6、mqtt
7、ws
主要用于H5等前端web。
NIO案例演示
修改ActiveMQ的配置文件
六、ActiveMQ的消息存储和持久化
MQ高可用主要从以下几方面入手:1、事务。2、持久(消息在MQ上持久)。3、签收。4、可持久化(消息持久化到物理存储)。
消息存储的持久化
为了避免意外宕机以后丢失信息,需要做到重启之后可以恢复消息队列,消息系统一般都会采用持久化机制。ActiveMQ的持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。
就是在发送者将消息发送出去以后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等再试图将消息发送给接收者,成功的时候将消息从存储中删除,失败则继续尝试发送。消息中心启动以后首先要检查指定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。
ActiveMQ的持久化策略有:
- AMQ Message Sort基于文件的存储方式,是以前的默认消息存储,现在不用了。
- KahaDB消息存储(系统默认),基于日志文件,从ActiveMQ5.4开始默认的持久化插件。KahaDB是目前默认的存储方式,适用于任何场景,提高性能和恢复能力。
消息存储使用一个事务日志和仅仅用一个索引文件来存储它所有的地址。KahaDB是一个专门针对消息持久化的解决方案,它对典型的消息使用模式进行了优化。数据被追加到data.logs中。当不需要log文件中的数据时,log文件会被丢弃(删除或丢弃)。 - JDBC消息存储
- LevelDB消息存储
- 带高速缓存的JDBC Message sort with ActiveMQ Journal
JDBC消息存储
当启动MQ的时候,会自动在仓库下建立三张表,并且把MQ数据同步到MySQL数据库中。
1、ACTIVEMQ_MSGS 消息表 队列和topic都在里面
2、ACTIVEMQ_ACKS
3、ACTIVEMQ_LOCK
当给MQ发送一条消息的时候,在数据库中也会存在相应的消息记录。
七、ActiveMQ多节点集群
为解决单点故障,ActiveMQ官网推荐配合Zookeeper进行主备形式的集群建设。
基于Zookeeper和LevelDB搭建ActiveMQ集群。提供主备方式的高可用集群功能,避免单点故障。
后面的自己动手吧。
八、MQ高级特性
1、Async Sends 异步投递
对于一个慢的消费者,使用同步发送消息可能出现消费阻塞的情况,慢消费者适合使用异步发送。
在连接的时候挂参数、在连接工厂设置、在连接上设置,均可以开启。
带回调消息发送者编码:
2、延时投递和定时投递
在activemq.xml文件中配置scheduleSupport属性为true
Java代码里面封装的辅助消息类型 ScheduleMessage
生产者编码
3、ActiveMQ消费者重试机制
九、死信队列
死信队列是一个用于处理失败消息的队列。
四个细节配置
共享死信队列策略
独立的死信队列策略
自动删除过期消息
存放非持久的消息到死信队列中
本文是尚硅谷周阳老师课程笔记总结,感谢!