Fork me on github

ActiveMQ(四)之 JMS

JMS

1. 思考JMS是什么?

  • JMS(java message service)java消息服务

  • 它是javaEE的一种技术

    扩展:想一想你熟悉的javaEE里有哪些技术?

    1. JDBC连接池
    2. EJB
    3. XML
    4. JMS
    5. .....
  • image-20220123193842912

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");
    
  • 消息体

    前提条件:

    1. 消息体就是封装具体的消息数据
    2. 消息有5种消息体格式
    3. 发送消息和接收消息的数据类型必须一致
    • 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. 如果想要发送除了消息以外的数据的话,就可以使用消息属性
// 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的消息持久性和非持久性

什么是持久性消息?

  1. 永久的保存,他会保存在文件中或者数据库中
  2. 生产者发送消息到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. 一定要先运行消费者,你只有向我注册了,我才会给你发送消息。就是这么任性(#.#)
  2. 运行完消费者后,再运行生产者
  3. 无论消费者是否在线,消费者最后都会收到消息(消费者上线后,会把之前离线的消息都接收过来)
// 生产者**************************
// 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();
// 消费者******************************

结果显示image-20220123205756322

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个部分:

  1. 消费者接受到这个消息
  2. 消费者处理这条消息
  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();那么这条消息就有可能会被重复接收
posted @ 2022-02-10 14:23  不想努力的小龙  阅读(85)  评论(0编辑  收藏  举报