ActiveMQ(四)之 JMS
JMS
1. 思考JMS是什么?
-
JMS(java message service)java消息服务
-
它是javaEE的一种技术
扩展:想一想你熟悉的javaEE里有哪些技术?
- JDBC连接池
- EJB
- XML
- JMS
- .....
2. JMS的组成结构和特点
1. JMS Provider
- 就是MQ服务器
- 它实现了JMS的接口和设置了一系列的规范
2. JMS Producer
- 消息的生产者
- 用于创建消息和发送消息
- 它是客户端应用
3. JMS Consumer
- 消息的消费者
- 用于接收消息和处理消息
- 它也是客户端应用
4. JMS Message
-
消息头
- JMSDestination:发送消息的目的地
// JMSDestination通过session会话对象创建 // QUEUE_NAME 表示队列的名字(自己取) // queue 队列 Queue queue = session.createQueue(QUEUE_NAME); // topic 话题 Topic topic = session.createTopic(QUEUE_NAME);
- JMSDeliveryMode:发送消息的模式
// 持久模式:可以永久保存。 // 非持久模式:一但服务崩溃,数据就会消失。 // 1. 发送消息的模式通过生产者设置 MessageProducer producer = session.createProducer(queue); // 非持久 producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 持久 producer.setDeliveryMode(DeliveryMode.PERSISTENT);
- JMSExpiration:设置消息在一定时间后过期,默认是永不过期
// 1. 过期时间通过消息对象设置 // 2. 如果发送后,在消息过期时间之后还没有被发送到目的地,则该消息被清除 TextMessage textMessage = session.createTextMessage("topic_name"); textMessage.setJMSExpiration(1000);
- JMSPriority:消息优先级,从0-9十个级别,0-4是普通消息5-9是加急消息
// 1. 必须保证加急消息必须要先于普通消息到达 // 2. 默认情况下,消息的优先级为 4 TextMessage textMessage = session.createTextMessage("topic_name"); textMessage.setJMSPriority(9);
- JMSMessageID: 消息唯一标识
// 1. 默认情况下MQ会给我们生成一个 TextMessage textMessage = session.createTextMessage("topic_name"); textMessage.setJMSMessageID("ABCD");
-
消息体
前提条件:
- 消息体就是封装具体的消息数据
- 消息有5种消息体格式
- 发送消息和接收消息的数据类型必须一致
- TextMessage:文本消息
// 1. 创建消息体 TextMessage textMessage = session.createTextMessage("topic_name--"); // 2. 生产者发送消息体 messageProducer.send(textMessage);
- MapMessage:类似于Map消息体
// 1. 创建消息体 MapMessage mapMessage = session.createMapMessage(); // 2. 设置 key-value, 消费者拿到消息后,通过get的方式拿到信息 mapMessage.setString("name", "张三" + 1); mapMessage.setInt("age", 18 + 1); // 3. 发送消息 messageProducer.send(mapMessage);
-
消息属性
它的作用:
- 如果想要发送除了消息以外的数据的话,就可以使用消息属性
// 1. 创建消息体
TextMessage textMessage = session.createTextMessage("topic_name--");
// 2. 设置消息属性
textMessage.setStringProperty("From","ZhangSan");
textMessage.setByteProperty("Spec", (byte) 1);
textMessage.setBooleanProperty("Invalide",true);
// 3. 发送消息
messageProducer.send(textMessage);
// 消费者*******************
// 消费者通过get的方式获取值>
textMessage.getStringProperty("From")
3. JMS的消息持久性和非持久性
什么是持久性消息?
- 永久的保存,他会保存在文件中或者数据库中
- 生产者发送消息到MQ消息中间件中后,无论消费者宕机还是MQ消息中间件掉线,都会保证数据不会丢失。
3.1 queue消息非持久和持久
- queue非持久(生产者发送消息后)
- 如果消息中间件宕机,消息就会丢失
- 如果消费者掉线,他会等到消费者上线,上线的消费者会收到消息
MessageProducer messageProducer = session.createProducer(queue);
// 非持久化
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
- queue持久化
- 当生产者发送消息后,MQ宕机重启后,消费者仍然会收到消息
MessageProducer messageProducer = session.createProducer(queue);
// 持久化
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
3.2 topic消息持久化
topic 默认是非持久的
需要注意的几点:
- 一定要先运行消费者,你只有向我注册了,我才会给你发送消息。就是这么任性(#.#)
- 运行完消费者后,再运行生产者
- 无论消费者是否在线,消费者最后都会收到消息(消费者上线后,会把之前离线的消息都接收过来)
// 生产者**************************
// 1. 创建工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 2. 创建连接对象
Connection connection = activeMQConnectionFactory.createConnection();
// 3. 创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4. 创建topic目的地
Topic topic = session.createTopic(TOPIC_NAME);
// 5. 创建生产者
MessageProducer messageProducer = session.createProducer(topic);
// 6. 建立连接
connection.start();
// 7. 发送消息……
//**************************************
- 消费者
// 消费者******************************
// 1. 创建工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 2. 创建连接对象
Connection connection = activeMQConnectionFactory.createConnection();
// 设置客户端id, 向MQ服务器注册自己的名称
connection.setClientID("marrry");
// 3. 创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4. 创建topic目的地
Topic topic = session.createTopic(TOPIC_NAME);
// 5. 创建一个订阅者对象(第一个参数是topic, 第二个参数是订阅者名称)
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic,"remark");
// 6. 在创建订阅者之前,在建立连接
connection.start();
// 7. 订阅者订阅 消息
message = topicSubscriber.receive();
// 消费者******************************
结果显示
4. 消息的事务性
- ACID原则
Atomicity: 原子性(要么全部执行,要么全部不执行)
Consitency:一致性(总和不变)
Isolation:隔离性(事务与事务的隔离)
Durablility: 持久性(执行后不可逆)
对于生产者开启事务:执行commit方法,这批消息才真正的被提交。不执行commit方法,这批消息不会提交。执行rollback方法,之前的消息会回滚掉。生产者的事务机制,要高于签收机制,当生产者开启事务,签收机制不再重要
消费者开启事务后,执行commit方法,这批消息才算真正的被消费。 队列中的消息就会一直重复消费
-
不开启事务会发生什么情况呢?
如果不开启事务,设置false,当生产者发送消息的时候,就是执行send()方法时,他会直接把消息发送到队列上去
// 在第三步,通过连接对象拿到session的时候,判断是否开启事务
// 第一个参数是事务, 第二个参数是签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
-
开启事务会发生什么情况呢?
如果开启事务的话,设置true,当生产者发送消息的时候,就是执行send()方法的时,他不会直接将消息发送到队列中,他会放在缓存中,只有执行了commit的时候,他才会真正的将消息发送到队列上去的
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
....
producer.send(textMessage); // 发送消息
....
try {
....
session.commit();
} catch (Exception e) {
System.out.println("出现异常,消息回滚");
// 工作中一般,当代码出错,我们在catch代码块中回滚。这样这批发送的消息就能回滚。
session.rollback();
} finally {
....关闭资源的操作
}
5. 消息的签收
5.1 消息的签收机制
一个消息被签收会进行3个部分:
- 消费者接受到这个消息
- 消费者处理这条消息
- 消息被签收(这一步可以有activemq服务器发起,也可以由消费者发起,这取决于以下的几种方式)
5.2 消息签收的几种方式
- 自动签收(Session.AUTO_ACKNOWLEDGE)
这是系统默认的方式,当消息从MessageConsumer的receive方法返回或者从MessageListener接口的onMessage方法返回时,会话自动确认消息签收
- 手动签收(Session.CLIENT_ACKNOWLEDGE)
手动签收。该种方式,需要我们手动调用Message.acknowledge(),来签收消息。如果不签收消息,该消息会被我们反复消费,只到被签收
- 运行重复消息(Session.DUPS_OK_ACKNOWLEDGE)
多线程或多个消费者同时消费到一个消息,因为线程不安全,可能会重复消费。该种方式很少使用到
- 事务下的消息 (Session.SESSION_TRANSACTED)
开始事务的情况下,可以使用该方式。该种方式很少使用到。
5.3 手动签收的例子
注意:消息签收是通过创建Session的时候设置的,消费者和生成者都要创建session,所以消费者和生成者都会设置消息签收, 但是最后的签收方式取决于消费者
// 生产者,此时设置的是自动签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
.....
// 消费者, 此时设置的是手动签收, 那么最后的签收方式就取决于手动签收
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
...
...
textMessage.acknowledge();
// 注意设置手动签收的时候,需要textMessage.acknowledge();表示我已经收到了这条消息
// 如果没有textMessage.acknowledge();那么这条消息就有可能会被重复接收