ActiveMQ
JMS编码总体结构
Queue
生产者
顺着打开,逆着关闭
消费者
public class JmsConsumer { public static final String ACTIVE_URL = "tcp://192.168.249.128:61616"; public static final String ACTIVE_QUEUE_NAMW = "queue01"; public static void main(String[] args) throws JMSException { ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVE_URL); Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = session.createQueue(ACTIVE_QUEUE_NAMW); //创建消费者 MessageConsumer consumer = session.createConsumer(queue); while (true){ TextMessage receive = (TextMessage) consumer.receive(3000); if (receive != null){ System.out.println("-----consumer:"+receive.getText()); }else break; } consumer.close(); session.close(); connection.close(); } }
Listener消费者
Topic
生产者
消费者
JMS开发的基本步骤
1、创建一个connection factory
2、通过connection factory来创建JMS connection
3、启动JMS connection
4、通过connection创建JMS session
5、创建JMS destination
6、创建JMS producer或者创建JMS message并设置destination
7、创建JMS consumer或者是注册一个JMS message listener
8、发送或者接受JMS message(s)
9、关闭所有的JMS资源
JMS的四大元素
-
JMS provider
-
实现JMS接口和规范的消息中间件,也就是我们的MQ服务器
-
-
JMS producer
-
消息生产者,创建和发送JMS消息的客户端应用
-
-
JMS consumer
-
消息消费者,接收和处理JMS消息的客户端应用
-
-
JMS message
-
消息头
-
JMSDestination——消息发送的目的地,主要是指Queue和Topic
-
JMSDeliveryMode
-
持久模式和非持久模式;
-
一条持久性的消息,应该被传送“一次仅仅一次”,这就意味着如果JMS提供者出现故障,该消息并不会丢失,它会在服务器恢复之后再次传播;
-
一条非持久的消息,最多会传送一次,这就意味着服务器如果出现故障,该消息将永久丢失;
-
-
JMSExpiration
-
消息过期时间;默认是永不过期;
-
消息过期时间,等于Destination的send方法中的timeToLive值加上发送时刻的GMT时间值;
-
如果timeToLive值等于0,则JMSExpiration被设为0,表示该消息永不过期;
-
如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被消除;
-
-
JMSPriority
-
消息优先级,从0—9十个级别,0—4是普通消息,5—9是加急消息;
-
JMS不要求MQ严格按照这十个优先级发送消息,但必须保证加急消息要优先于普通消息到达。默认是4级
-
-
JMSMessageID
-
唯一识别每个消息的标识由MQ产生;
-
-
-
消息体
-
封装具体的消息数据;
-
5中消息体格式;
-
TextMessage
-
普通字符串消息,包含一个String
-
-
MapMessage
-
一个Map类型的消息,key为string类型,而值为Java的基本类型
-
-
BytesMessage
-
二进制数组消息,包含一个byte[]
-
-
StreamMessage
-
java数据流消息,用标准流操作来顺序的填充和读取
-
-
ObjectMessage
-
对象消息,包含一个可序列化的Java对象
-
-
-
发送和就收的消息体类型必须一致对应;
-
-
消息属性
-
如果需要除了消息头字段以外的值,那么可以使用消息属性;
-
识别/去重/重点标注等操作非常有用的方法;
-
-
JMS的可靠性
-
持久性
-
参数设置说明
-
非持久:当服务器宕机,消息不存在
-
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
-
持久:当服务器宕机,消息依然存在
-
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
-
-
持久的Queue
-
队列默认是持久化的
-
-
持久的Topic
-
代码
-
先启动订阅再启动生产
-
持久的发布主题生产者
-
持久的订阅主题消费者
-
-
控制台
-
类似微信公众号订阅发布
-
-
-
事务
-
producer提交时的事务
-
false
-
只要执行send,就进入到队列中
-
关闭事务,那第2个签收参数的设置需要有效
-
-
true
-
先执行send再执行commit,消息才被真正的提交到队列中(在session关闭之前,使用session.commit();进行提交)
-
消息需要批量发送,需要缓冲区处理
-
-
-
事务偏生产者,签收偏消费者
-
-
签收
-
非事务
-
自动签收(默认)
-
Session.AUTO_ACKNOWLEDGE
-
-
手动签收
-
Session.CLIENT_ACKNOWLEDGE
-
客户端调用acknowledge方法手动签收
-
-
允许重复消息
-
Session.DUPS_OK_ACKNOWLEDGE
-
-
-
事务
-
生产事务开启,只有commit后才能将全部消息变为已消费
-
消息生产者
-
消息消费者
-
-
签收和事务关系
-
在事务性会话中,当一个事务被成功提交则消息被自动签收;如果事务回滚,则消息会被再次传送;
-
非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode);
-
-
JMS的点对点总结
点对点模型是基于队列的,生产者发消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能;和我们平时给朋友发消息类似;
1、如果在Session关闭时有部分消息已被接收到但还没有被签收,那当消费者下次连接到相同队列时,这些消息还会被再次接收;
2、队列可以长久的保存消息到消费者收到消息,消费者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势;
JMS的发布订阅总结
JMS Pub/Sub模型定义了如何向一个内容节点发布订阅消息,这些节点被称作topic;
主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息;
主题使得消息订阅者和消息发布者保持相互独立,不需要接触即可保证消息的传送;
非持久订阅
非持久订阅只有当客户端处于激活状态,也就是MQ保持连接状态才能收到发送到某个主题的消息
如果消费者处于离线状态,生产者发送的主题消息将会丢失作废,消费者永远不会收到;
一句话:先要订阅注册才能接受到发布的消息,只给订阅者发布消息;
持久订阅
客户端首先向MQ注册一个自己的身份ID识别号,当这个客户端处于离线时,生产者为这个ID保存所有发送到主题的消息;
当客户再次连接到MQ时会根据消费者的ID得到所有当自己处于离线时发送到主题的消息;
非持久订阅状态下,不能恢复或重新派发一个未签收的消息;
持久订阅才能恢复或重新派送一个未签收的消息;
用哪个
当所有的消息必须被接收,则用持久订阅。当丢失消息能够被容忍,则用非持久订阅;
ActiveMQ的Broker
什么时broker
相当于一个ActiveMQ服务器实例;
就是Broker实现了用代码的形式启动ActiveMQ将MQ嵌入到Java代码中,以便随时用随时启动;
在用的时候去启动,这样能够节省资源,也保证了可靠性;
不同的conf配置文件模拟不同的实例
嵌入Broker
用ActiveMQ Broker作为独立的消息服务器来构建Java应用;
ActiveMQ也支持vm中通信基于嵌入式的broker,能够无缝的集成其他java应用;
-
POM.xml
-
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version> </dependency>
-
-
EmbedBroker
-
-
队列验证
spring整合ActiveMQ
Maven修改,需要添加spring支持JMS包
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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.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/task http://www.springframework.org/schema/task/spring-task.xsd"> <context:component-scan base-package="cn.spring"/> <!-- 配置生产者--> <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://192.168.249.128: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="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>
队列
生产者
消费者
主题
在spring里面实现消费者不启动,直接通过配置监听完成
springboot整合ActiveMQ
队列
队列生产者
-
新建maven工程并设置包名和类名
-
工程名:boot_mq_produce
-
包名:com.baidu.boot.activemq
-
-
pom.xml
-
application.yml
-
server: port: 8088 spring: activemq: broker-url: tcp:192.168.249.128:61616 user: admin password: admin jms: pub-sub-domain: false #false = Queue ; true = Topic #自己定义队列名称 myqueue: boot-activemq-queue
-
-
配置Bean
-
@Component public class ConfigBean { @Value("${myqueue}") private String myQueue; @Bean public Queue queue(){ return new ActiveMQQueue(myQueue); } }
-
-
Queue_Produce
-
@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)); } }
-
-
主启动类MainApp_Produce
-
测试单元
-
定时投递
-
@Scheduled(fixedDelay = 3000) public void produceMsgScheduled(){ jmsMessagingTemplate.convertAndSend(queue, "******Scheduled:"+ UUID.randomUUID().toString().substring(0,6)); System.out.println("********send ok"); }
-
队列消费者
-
新建maven工程并设置包名和类名
-
工程名:boot_mq_consumer
-
包名:com.baidu.boot.activemq
-
-
pom.xml
-
application.yml
-
Queue_Comsumer
-
@Component public class Queue_Consumerr { @JmsListener(destination = "${myqueue}") public void receive(TextMessage textMessage) throws Exception{ System.out.println("******消费者收到消息:"+textMessage.getText()); } }
-
主启动类
发布订阅
Topic生产者
-
新建maven工程并设置包名和类名
-
工程名:boot_mq_topic_produce
-
包名:com.baidu.boot.activemq.topic.produce
-
-
pom.xml
-
application.yml
-
配置Bean
-
Topic_Produce
-
主启动类MainApp_TopicProduce
Topic消费者
ActiveMQ的传输协议
面试题
默认的61616端口如何修改
你生产的链接协议如何配置的?使用tcp么?
官网
是什么
-
ActiveMQ支持的client-broker通讯协议有:TCP、NIO、UDP、SSL、Http(s)、VM。
-
其中配置Transport Connector的文件在activeMQ安装目录的conf/activemq.xml中的<transportConnectors>标签之内;
-
有哪些
Transmission Control Protocol(TCP)默认
-
这是默认的Broker配置,TCP的Client监听端口61616
-
在网络传输数据前,必须要序列化数据,消息是通过一个叫wire protocol的来序列化成字节流;默认情况下ActiveMQ把wire protocol叫做OpenWire,它的目的是促使网络上的效率和数据快速交互;
-
TCP连接的URI形成如:tcp://hostname:port?key=value&key=value,后面的参数是可选;
-
TCP传输的优点:
-
TCP协议传输可靠性高,稳定性强;
-
高效性:字节流方式传输,效率很高;
-
有效性、可用性:应用广泛,支持任何平台;
-
-
关于Transport协议的可配置参数可以参考官网:ActiveMQ (apache.org)
New I/O API Protocol(NIO)
-
NIO协议和TCP协议类似但NIO更侧重于底层的访问操作,它允许开发人员对同一资源可有更多的client调用和服务端有更多的负载;
-
适合使用NIO协议的场景:
-
可能有大量的Client去连接到Broker上,一般情况下,大量的Client去连接Broker是被操作系统的线程所限制的;因此,NIO的实现比TCP需要更少的线程去运行,所以建议使用NIO协议;
-
可能对于Broker有一个很迟钝的网络传输,NIO比TCP提供更好的性能;
-
-
NIO连接的URI形式:nio://hostname:port?key=value
-
Transport Connector配置示例,参考官网:ActiveMQ (apache.org)
AMQP协议
stomp协议
Secure Sockets Layer Protocol(SSL)
mqtt协议
ws协议
小结
NIO案例演示
NIO案例演示增强
-
如何让这个端口支持NIO网络IO模型,又让它支持多个协议呢?
-
使用auto关键字
-
使用“+”符号来为端口设置多种特性
-
如果我们既需要某一个端口支持NIO网络IO模型,又需要它支持多个协议;
-
-
ActiveMQ的消息存储和持久化
有哪些
AMQ Message Store(了解)
-
基于文件的存储方式,是以前的默认消息存储,现在不用了;
KahaDB(默认)
-
基于日志文件,从ActiveMQ5.4开始默认的持久化插件
-
验证
-
-
说明
-
KahaDB是目前默认的存储方式,可用于任何场景,提高性能和恢复能力;
-
消息存储使用一个事务日志和仅仅用一个索引文件来存储它所有的地址;
-
KahaDB是一个专门针对消息持久化的解决方案,它对典型的消息使用模式进行了优化;
-
数据被迫加到data logs中,当不再需要log文件中的数据的时候,log文件会被丢弃;
-
-
KahaDB的存储原理
-
kahadb在消息保存目录中只有4类文件和一个lock,跟ActiveMQ的其他几种文件存储引擎相比这就非常简洁了;
-
1、db-<Number>.log KahaDB存储消息到预定义大小的数据记录文件中,文件命名为db-<Number>.log。当数据文件已满时,一个新的文件会随之创建,number数值也会随之递增,它随着消息数量的增多,如每32M一个文件,文件名按照数字进行编号,如db-1.log\db-2.log...。当不再有引用到数据文件中的任何消息时,文件会被删除或归档;
-
2、db.data该文件包含了持久化的BTree索引,索引了消息数据记录中的消息,它是消息的索引文件,本质上是BTree(B树),使用B-Tree作为索引指向db-<Number>.log里面存储的消息;
-
3、db.free当前db.data文件里哪些页面是空闲的,文件具体内容是所有空闲页的ID;
-
4、db.redo用来进行消息恢复,如果KahaDB消息存储在强制退出后启动,用于恢复BTree索引;
-
5、lock文件锁,表示当前获得kahadb读写权限的broker;
-
JDBC消息存储
-
消息基于JDBC存储的
-
1、MQ+MySQL
-
2、添加mysql数据库的驱动到lib文件夹
-
3、jdbcPersistenceAdapter
-
4、数据库连接池配置
-
5、建仓SQL和建表说明
-
6、代码运行验证
-
7、数据库情况
-
8、小结
LevelDB消息存储(了解)
-
这种文件系统是从ActiveMQ5.8之后引进的,它和KahaDB非常相似,也是基于文件的本地数据库存储形式,但是他提比kahadb更快的持久性;
-
但它不使用自定义B-Tree实现来索引预写日志,而是使用基于LevelDB的索引;