ActiveMQ--JMS规范如何确保消息的可靠性

一、JMS规范

JavaEE 是一套使用 Java进行企业级开发的13个核心规范工业标准,包括:

JDBC 数据库连接

JNDI Java的命名和目录接口

EJB Enterprise java bean

RMI 远程方法调用 一般使用TCP/IP协议

Java IDL 接口定义语言

Jsp

Servlet

XML

JMS Java消息服务

JTA

JTS

JavaMail

JAF

JMS规范介绍

JMS 部件

JMS provider

JMS producer

JMS consumer

JMS message

含义

实现JMS 的消息中间件,也就是MQ服务器

消息生产者,创建和发送消息的客户端

消息消费者,接收和处理消息的客户端

JMS 消息,分为消息头、消息属性、消息体

JMS message的消息头

消息头

JMSDestination

JMSDeliveryMode

JMSExpiration

JMSPriority

JMSMessageId

含义

头在哪儿

是持久还是非持久

过期时间,默认永久

优先级,默认是4

有0~9 ,5-9 是紧急的,0-4 是普通的

唯一的消息ID

消息体;封装具体的消息数据

5种消息体

TextMessage

Mapmessage

BytesMessage

StreamMessage

ObjectMessage

含义

普通字符串消息,包含一个String

Map 类型的消息,

k-> String

v -> Java 基本类型

二进制数组消息,包含一个byte[]

Java 数据流消息,用标准流操作来顺序的填充读取

对象消息,包含一个可序列化的Java 对象

发送和接收的消息类型必须一致

消息属性:可以自行封装消息携带的其他属性信息

二、通过JMS规范是如何确保消息的可靠性

JMS可靠性通过:Persistent持久性、事务、Acknowledge 签收来保证的

1.消息的持久化

1)对于queue模式的消息

// 在队列为目的地的时候持久化消息
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);

// 队列为目的地的非持久化消息
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

queue形式的消息,默认为持久化的,服务器宕机后消息依旧存在,只是没有入队,当服务器再次启动,消费者还可以对消息进行消费。

但是非持久化的消息,服务器宕机后消息永远丢失。

2)对于目的地为主题(topic)来说,默认就是非持久化的,让主题的订阅持久化的意义在于:对于订阅了此主题的人来说,就算客户端没有在线,生产者端生产了消息,在客户端上线后依然可以收到在此期间发布的消息。

生产者代码实现

public class TopicProducer {
    public static final String ACTIVE_URL = "tcp://192.168.6.10:61616";
    public static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException {
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVE_URL);
        Connection connection = activeMQConnectionFactory.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 = 0; i < 3; i++) {
            TextMessage textMessage = session.createTextMessage("msg---" + i);
            producer.send(textMessage);
        }

        producer.close();
        session.close();
        connection.close();
        System.out.println("发送持久化topic完成~");

    }
}

消费者代码实现

public class TopicConsumer {
    public static final String ACTIVE_URL = "tcp://192.168.6.10:61616";
    public static final String TOPIC_NAME = "topic01";
    
    public static void main(String[] args) throws JMSException {
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVE_URL);
        Connection connection = activeMQConnectionFactory.createConnection();
        //要设置一个订阅该主题的订阅者Id
        connection.setClientID("xijian");
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(TOPIC_NAME);
        //这里与之前不同,要通过session创建一个持久化的订阅者
        //参数为订阅的主题与备注
        TopicSubscriber t = session.createDurableSubscriber(topic, "我是xijian");
        connection.start();
        //阻塞式接收消息
        Message message = t.receive();

        while(message != null){
            TextMessage textMessage = (TextMessage) message;
            System.out.println("收到的持久化topic :"+textMessage.getText());
            message = t.receive();
        }


    }

}

因为topic主题这种模式是基于发布/订阅的,如果想要消息被消费,仍需要先订阅到这个主题,再进行消息的消费,当启动客户端时,我们通过访问其web控制台查看一下

可以看到,上线的订阅者有我刚刚运行的客户端。

这时启动服务端发送消息,客户端就可以收到了,客户端如果下线,也会在下次上线前接收到发布的消息,重点就是要设置消息的持久化,如果不设置持久化,那这个订阅信息是不会被保留的,下线即失效。

2.事务

createSession的第一个参数为true 为开启事务,开启事务之后必须在将消息提交,才可以在队列中看到消息。

Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

事务的提交

session.commit();

事务开启的意义在于,如果对于多条必须同批次传输的消息,可以使用事务,如果一条传输失败,可以将事务回滚,再次传输,保证数据的完整性。

1)对于生产者来说

消息生产者偏重于事务,开启事务后send的消息,只有在session执行commit后才会被提交到服务器,不然无效。

2)对于消费者来说

消费者偏重于签收,但开启事务后,消息是否被消费就只和是否commit有关了,消息在commit后会自动签收,如回滚,则消息会被继续传送,如不开启事务,则需要通过签收来进行消息的消费(下文会提到)

3.签收

签收一般会在非事务状态下生效,且主要针对于消费者,生产者的签收功能并没有什么意义,因为如果开启事务的话,签收与否取决于是否提交和回滚。

签收一共有四种状态,最常用的签收状态有两种

Session.AUTO_ACKNOWLEDGE      自动签收

Session.CLIENT_ACKNOWLEDGE     手动签收
手动签收需要acknowledge   
textMessage.acknowledge();

在非事务状态下,如果设置了手动签收,那必须在recieve到一条消息后进行签收,不然消息仍然是未被消费的状态,下次消费时依旧能获取到没被签收的消息。

总结:在ActiveMQ保证消息可靠性的四种方式

1)进行消息的持久化:queue默认为持久化,topic模式下需要手动设置

2)开启消息的事务

3)在消费者的非事务状态下要开启消息手动签收功能

4)集群搭建实现高可用

发布了227 篇原创文章 · 获赞 77 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/m2606707610/article/details/103468473
今日推荐