消息中间件ActiveMQ学习笔记(二) [Java编码MQ,消费者生产者基本模型]

近期计划学习一下消息队列;
找到的学习视频地址:尚硅谷ActiveMQ教程快速入门



1.Java编码MQ,模拟基础生产者消费者


在这里插入图片描述

  • 创建一个Connection Factory连接工厂;
  • 然后通过连接工厂创建connection连接;
  • 启动该链接后,可通过连接出session 会话;
  • 创建destination目的地[可理解为队列/一种topic主题];
  • 创建出生产者producer / message消息,然后设置destination目的地;
  • 创建出消费者consumer,或者注册一个消息监听器message listener;
  • 生产者可以发送资源,消费者接收消息;
  • 最终完成操作后,可以关闭连接.


首先创建一个简单的maven项目;
在pom.xml文件中使用下面的依赖;

<dependencies>
     <!-- activemq使用的jar包-->
     <dependency>
         <groupId>org.apache.activemq</groupId>
         <artifactId>activemq-all</artifactId>
         <version>5.16.4</version>
     </dependency>
     <!-- activemq和 spring整合-->
     <dependency>
         <groupId>org.apache.xbean</groupId>
         <artifactId>xbean-spring</artifactId>
         <version>3.16</version>
     </dependency>
     <!--日志-->
     <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.7.25</version>
     </dependency>
     <dependency>
         <groupId>ch.qos.logback</groupId>
         <artifactId>logback-classic</artifactId>
         <version>1.2.3</version>
     </dependency>
     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.22</version>
     </dependency>
     <!--测试-->
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
     </dependency>
</dependencies>

实际操作;

自定义消息生产者


就从这个MQ工厂类ActiveMQConnectionFactory来看;

无参构造中提示需要使用这样一个常量;DEFAULT_BROKER_URL

public static final String DEFAULT_BROKER_URL = "failover://"+DEFAULT_BROKER_BIND_URL;

自定义写一个MQ的生产者;

package com.xiaozhi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * @BelongsProject: activemqstudyday1
 * @BelongsPackage: com.xiaozhi.activemq
 * @Author: 信计1801 李智青
 * @Date: 2022/4/7 16:02
 * @Description: 自定义生产者
 */
public class MyProduce {
    //链接url
    private static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    private static final String QUEUE_NAME = "myDeque";

    public static void main(String[] args) throws JMSException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);

        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;例如:队列
        Destination destination = session.createQueue(QUEUE_NAME);
        //5.创建消息生产者;
        MessageProducer producer = session.createProducer(destination);
        //生产6条消息存入到MQ中间件的队列中;
        for (int i = 0; i <= 5; i++) {
            //表名是第几条消息;
            TextMessage textMessage = session.createTextMessage("this is the" + i + "message");
            producer.send(textMessage);
        }
        //关闭资源;
        producer.close();
        session.close();
        connection.close();

        System.out.println("+-+-+-+-+-+-+-+-+-+我把消息都发给中间大佬MQ了");
    }
}

编码完成后, 打开我linux上的ActiveMQ,启动;
本地访问http://192.168.59.128:8161/admin/queues.jsp;注意此时还没有消息;
在这里插入图片描述
现在运行自定义的生产者代码;

麻了,一开始运行老报错,连接超时;
然后去linux 在防火墙设置开放端口61616

firewall-cmd --zone=public --add-port=61616/tcp --permanent

然后重启防火墙

 firewall-cmd --reload

运行Java代码;
在这里插入图片描述
再次刷新访问页面,可看到消息已发布出去了;
在这里插入图片描述
其中的几个参数:
Number Of Pending Messages:等待消费的消息,即未出队列的数量 =总接收数-总出队列数。
Number Of Consumers:消费者数量

Messages Enqueued进队列的总消息量,包括出队列的.
Messages Dequeued:出队消息数,即消费者使用的数量


自定义同步阻塞式的消息消费者


package com.xiaozhi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * @BelongsProject: activemqstudyday1
 * @BelongsPackage: com.xiaozhi.activemq
 * @Author: 信计1801 李智青
 * @Date: 2022/4/7 18:14
 * @Description: 自定义消费者
 */
public class MyConsumer {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String QUEUE_NAME = "myDeque";

    public static void main(String[] args) throws JMSException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);

        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;例如:队列
        Destination destination = session.createQueue(QUEUE_NAME);
        //创建消费者;
        MessageConsumer consumer = session.createConsumer(destination);
        while (true) {
            //消费者接收消息;
            Message message = (TextMessage) consumer.receive();
            if (message != null) {
                System.out.println("嘿嘿,我收到了-->" + message);
            } else {
                break;
            }
        }
        //关闭资源;
        consumer.close();
        session.close();
        connection.close();

    }
}

运行代码,消息已接收完成

在这里插入图片描述

在这里插入图片描述

注意到这个程序一直在运行监听状态,并没有停止;所以访问时看到的消费者值为1一直存在;
在这里插入图片描述

停止运行后,再次查看,消费者数量已显示为0;
在这里插入图片描述在这里插入图片描述


现在重新将生产者代码运行一下;
在这里插入图片描述

在消费者接收消息时;这里使用6秒如果还没有收到消息就自动关闭;
在这里插入图片描述
去访问时发现消费者数量已经为0了;
在这里插入图片描述


异步监听方式的消费者


刚才那样的消费者接收消息为同步阻塞方式;

改为监听者模式

public class MyConsumer {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String QUEUE_NAME = "myDeque";

    public static void main(String[] args) throws JMSException, IOException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);

        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;例如:队列
        Queue destination = session.createQueue(QUEUE_NAME);


        //创建消费者;
        MessageConsumer consumer = session.createConsumer(destination);
        //设定监听者;
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if(null != message && message instanceof TextMessage){
                    //进行强制类型转换;
                    TextMessage textMessage = (TextMessage)message;
                    try {
                        System.out.println("接收者来接收消息"+textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //保持控制台不关闭;
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

运行后;

在这里插入图片描述

若是将System.in.read();这行代码注释掉;

在这里插入图片描述
这里消费者刚启动就关闭了;
在这里插入图片描述
导致消息队列中的消息没有被接收处理;

在这里插入图片描述


关于3种常见的消费者问题


案例1: 先生产, 仅启动1号消费者, 是可以完成消息的消费.

先生产3条消息;
在这里插入图片描述

OK,这里有3条待消费处理的消息.
在这里插入图片描述
在运行一次生产者;现在队列中有6条消息;
在这里插入图片描述

然后此时启动消费者;
在这里插入图片描述
完成消费;
在这里插入图片描述

OK,完毕,清空队列
在这里插入图片描述


案例2: 先生产, 启动1号消费者之后, 然后启动2号消费者; 那么2号消费者 是无法进行消费的.

  • OK,这次先生产3条消息:
    在这里插入图片描述

  • 让1号消费者先出动,成功消费;
    在这里插入图片描述
    在这里插入图片描述

  • 然后让2号消费者出动;无法消费;
    在这里插入图片描述
    在这里插入图片描述


案例3: 先启动2个消费者,然后在生成6个消息; 2个消费者平均分配消费到一半的消息.

启动消费者1号;
在这里插入图片描述

启动消费者2号;
在这里插入图片描述

目前已经显示两个消费者;
在这里插入图片描述

生产6条消息;
在这里插入图片描述

查看消费状况:1号消费者消费到 1,3,5的消息
在这里插入图片描述

2号消费者消费到 2,4,6的消息
在这里插入图片描述

此时可看到这个队列的状态.
在这里插入图片描述


队列案例总结


两种消费方式:

  • 同步阻塞方式 :receive() , 订阅者或者接受者调用MessageConsumer的receive() 方法接收消息,receive方法在接收到消息之前,将会一直阻塞.
  • 异步非阻塞方式: 采用监听器onMessage() , 订阅者或接收者可以通过MessageConsumer的setMessageListener(MessageListener listener) 注册消息监听器, 当这个消息到达时,当前系统就会自动调用监听器MessageListeneronMessage(Message message)方法

点对点消息传递域:

  • 每个消息只有一个消费者,1对1的关系.
  • 消息的生产者和消费者之间没有时间上的相关,无论消费者在生产者发送消息时是否处于运行状态,消费都可以提取消息.
  • 消息被消费了之后,队列中就不会再存储,也就是说: 消费者无法消费 已经被消费的消息.
    在这里插入图片描述

2.Topic 主题


上面主要是队列(Queue)的入门学习部分:
在这里插入图片描述

现在来看看主题(Topic).
在这里插入图片描述


  • 生产者将消息发布到主题topic之后,可以由多个消费者前来消费, 一对多的关系;
  • 生产者与消费者具有时间相关性质,订阅某主题的消费者只能消费到订阅之后发布的消息.[ 比如说你加了一个群,但是这个群聊很久之前就有很多聊天记录,但你是看不到的.]
  • 生产者生产消息时,topic主题不会保存消息, 若此时无人订阅,那么生产出来的就是废弃消息, 所以说一般要先启动消费者再去启动生产者.

在JMS的规范中,可支持用户创建持久性的订阅, 允许消费者去消费没有处于激活状态时发送的消息.

在这里插入图片描述


完成一个基础的生产者-消费者-发布与订阅关系的案例

生产者[发布者]:

public class MyProduce {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String TOPIC_NAME = "myTopicOne";

    public static void main(String[] args) throws JMSException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);

        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地; 主题
        Topic destination = session.createTopic(TOPIC_NAME);
        //5.创建消息生产者;
        MessageProducer producer = session.createProducer(destination);
        //生产消息存入到MQ中间件的主题中;
        for (int i = 1; i <= 3; i++) {
            //表名是第几条消息;
            TextMessage textMessage = session.createTextMessage("+this is the" + i + "message");
            producer.send(textMessage);
        }
        //关闭资源;
        producer.close();
        session.close();
        connection.close();

        System.out.println("+-+-+-+-+-+-+-+-+-+主题消息生成之后,发给中间大佬MQ了");
    }
}

消费者[订阅者]:

public class MyConsumer {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String Topic_NAME = "myTopic";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是消费者->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;--->主题
        Topic destination = session.createTopic(Topic_NAME);
        //创建消费者;
        MessageConsumer consumer = session.createConsumer(destination);
        //设定监听者;
        consumer.setMessageListener((message)->{
            if(null != message && message instanceof TextMessage){
                //进行强制类型转换;
                TextMessage textMessage = (TextMessage)message;
                try {
                    System.out.println("接收者来接收消息"+textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        //保持控制台不关闭;
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}
  • 首先启动3个消费者;
    在这里插入图片描述

  • 在linux中启动MQ,本地访问http://192.168.59.128:8161/admin/topics.jsp; 可看到目前有两个消费者

在这里插入图片描述

  • 此时启动生产者;
    在这里插入图片描述
  • 可看到,每个订阅的消费者都可以收到消息;
    在这里插入图片描述

所以消息出队为9条;
在这里插入图片描述


删除主题中的消息;试试这个案例; 先生产后再消费, 那么消费者是否可以消费到;
然后在启动生产者,此时消费者是否能接受到消息呢???

  • 先启动生产者.
    在这里插入图片描述
    在这里插入图片描述

  • 启动消费者

在这里插入图片描述

  • 此时可注意到,消费者即使启动,但是无法接受到消息.
    在这里插入图片描述
  • 那么此时再启动生产者,来生产一波新的消息;
    在这里插入图片描述
    注意到,消费者只能消费到后来生产的3条消息;
    在这里插入图片描述
    在这里插入图片描述

队列.主题 比较


比较Topic主题Queue队列
工作模式发布-订阅模式,若此时无订阅者,则会将消息丢弃,若有多个订阅者,那这些订阅者都会收到消息采用负载均衡模式,若没有消费者,则消息不会被丢弃,若有多个消费者,那么该消息仅发送给其中一个消费者,且该消费者需要回复ACK确认消息
状态有状态队列的数据默认在MQ服务器中,以文件形式保存,比如ActiveMQ,一般来说保存在$AMQ_HOME\data\kr-store\data目录下,也可配置为DB数据存储
传递完整性若没有订阅者,则该消息会被丢弃不会丢弃消息
处理的效率由于消息按照订阅者数量进行复制,所以处理性能将会跟随订阅者的增加而降低,那么就需要结合不同消息协议自身的性能差异由于一条消息仅可发送到一个消费者,即使增加消费者,性能也不会大幅度降低.

MQ产品比较


特性ActiveMQRabbitMQKafKaRocketMQ
PRODUCER-CUMSUMER支持支持支持支持
PUBLISH-SUBSCRIBE支持支持支持支持
REQUEST-REPLY支持支持支持
API完整性低,静态配置
多语言支持支持,且优先Java无关语言支持,且优先Java支持
单机吞吐量万级万级十万级单机万级
消息延迟微秒级毫秒级
可用性高可用-主从高可用–主从高可用–分布式高可用
消息丢失率理论上不存在
消息重复可控制理论上会重复
文档完备性
部署难度

JMS


JMS:Java消息服务
之前学习过JavaSE基础部分; JavaEE企业级开发应用;而 JMS也是JavaEE的一部分;
具体指的是两个应用程序之间进行异步通信的API规范,作为标志协议与消息服务提供一组同样接口,包含创建-发送-读取消息等,用于支持Java应用程序开发,在JavaEE中,当两个应用程序使用了JMS通信,实际通过共同的消息收发组件关联->解耦削峰异步效果.
在这里插入图片描述

JMS的组成结构,四部分:

  • JMS-provider (提供者) : 即实现JMS接口/规范的消息中间件MQ;
  • JMS-producer(生产者): 即消息的生产者,创建以及发送JMS消息的客户端应用.
  • JMS-Consumer(消费者):消息消费者,可接受处理JMS消息的客户端应用;
  • JMS Message(具体消息):分为消息头,消息属性以及消息体.

消息头


消息头的范围:

  • JMSDestination:消息目的地
  • JMSDeliveryMode:消息持久化模式
  • JMSExpiration:消息过期时间
  • JMSPriority:消息的优先级
  • JMSMessageID:消息的唯一标识符。后面我们会介绍如何解决幂等性。

在消息的生产者中可以 设置属性,消息的消费者可以 获取属性,
这些属性在 send 方法里面也可以设置。

  • 可设置消息属性
    在这里插入图片描述

可以在send()方法中设置:
在这里插入图片描述


消息的持久化模式:

  • 当消息被持久化之后,就仅被传送一次, 若JMS的提供者出现问题,该消息并不会丢失,会在服务器恢复之后再次传递.
  • 若消息没有被持久化,那么最多就传送一次,若服务器出现故障,则该消息永久性丢失.

消息过期时间设置:

  • 可设置消息在指定时间后过期,默认情况下消息不过期;
  • 设定的消息过期时间,即Destination调用的send()方法中的timeToLive值加入发送时间的GMT时间值;
  • timeToLive的值变为0,则将JMSExpiration 设置为0, 表明消息永远不会过期;
  • 若在发送之后,消息过期了,但是还没有发到目的地,那么就会删除该消息.

消息的优先级:

  • 消息的优先级属性,分为0~9十个级别, 从0-4是普通消息,5-9是优先处理的消息;默认级别为 4.
  • JMS不会严格要求按照优先级发送消息,但是会保证优先处理的消息 比 普通消息先到达.

消息体


在消息体中封装了具体的消息数据;类型格式分为5种;注意发送–接收时的类型格式要一致;

  • (1)TextMessage : 字符串类型,String;
  • (2)MapMessage : Map类型的类型,以String类型为key,Java基本类型都可作为值;
  • (3)BytesMessage : 二进制数组类型,byte[].
  • (4)StreamMessage : Java数据流类型,采用标准流顺序操作数据的填充/消息.
  • (5)ObjectMessage : 对象类型,存储为可序列化的Java对象.

案例:
生产String文本类型和Map类型的消息;

public class MyProduce {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String QUEUE_NAME = "myDeque";

    public static void main(String[] args) throws JMSException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);

        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;例如:队列
        Destination destination = session.createQueue(QUEUE_NAME);
        //5.创建消息生产者;
        MessageProducer producer = session.createProducer(destination);
        //生产消息存入到MQ中间件的队列中;
        for (int i = 1; i <= 3; i++) {
            //表名是第几条消息;
            TextMessage textMessage = session.createTextMessage("+this is the" + i + "字符串类型的message");
            producer.send(textMessage);

            //使用Map类型的参数
            MapMessage mapMessage = session.createMapMessage();
            mapMessage.setString("key1", "Map类型的消息值-->val1");
            producer.send(mapMessage);
        }
        //关闭资源;
        producer.close();
        session.close();
        connection.close();

        System.out.println("+-+-+-+-+-+-+-+-+-+我把消息都发给中间大佬MQ了");
    }
}

在这里插入图片描述

注意,发什么类型格式的消息,就怎么接收;

public class MyConsumer {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String QUEUE_NAME = "myDeque";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是消费者->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;例如:队列
        Queue destination = session.createQueue(QUEUE_NAME);
        //创建消费者;
        MessageConsumer consumer = session.createConsumer(destination);
        //设定监听者;
        consumer.setMessageListener((message) -> {
            //String文本类型的;
            if (null != message && message instanceof TextMessage) {
                //进行强制类型转换;
                TextMessage textMessage = (TextMessage) message;
                try {
                    System.out.println("接收到Stringle类型的消息>>>>" + textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }

            //Map类型的;
            if (null != message && message instanceof MapMessage) {
                //进行强制类型转换;
                MapMessage mapMessage = (MapMessage) message;
                try {
                    System.out.println("接收到Map类型的消息>>>>" + mapMessage.getString("key1"));
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        //保持控制台不关闭;
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

在这里插入图片描述

队列消费情况
在这里插入图片描述


消息属性


可以使用消息属性得到 除了消息子段之外的值, 可进行识别/去重/重点标注操作.
以属性名-属性键值对方式制定, 实际可以将属性看做是扩展的消息头;属性中存在着 消息头没有的附加信息;例如在属性中设置消息选择器; 而消息的属性就像是分配给一条消息的附加消息头.

比如在生产者中设置属性;
在这里插入图片描述


案例:使用消息属性

  • 在生产者中设定发送的消息属性
    在这里插入图片描述

  • 在消费者中设定接收消息属性;
    在这里插入图片描述

先启动生产者,
在这里插入图片描述
在这里插入图片描述

启动消费者;得到消息属性;
在这里插入图片描述
在这里插入图片描述


还记得刚开始的入门连接案例中,在创建会话时使用的一个方法,一个参数为事务,一个参数为签收机制.
在这里插入图片描述


消息的持久化与非持久化


持久化消息:

  • 也就是保证消息仅被传送一次成功使用一次, 当持久化的消息传送到目标时, 消息服务会将它放入持久性数据存储,若消息服务由于某种原因导致失败,可恢复这个消息并且将这个消息传输给对应的消费者,即使增加了开销,但可靠性提升了.
  • 当消息的生产者将消息成功发给MQ后, 也许会有这些问题–>[ MQ服务器宕机, 消费者掉线,] 实际上消息消费者都可以成功得到消息.(注意topic状态下,必须保证消费者先注册); 若是生产者在发送消息时就已经失败,那么消费者就不会得到这些消息.

案例(1): 设置非持久化模式.

producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
  • 在生产者被设定时,搭配非持久化模式;
    在这里插入图片描述

  • OK,现在启动生产者,生产3条消息;
    在这里插入图片描述

  • 现在模拟MQ服务器宕机; 使用命令停止mq服务.

./activemq stop

在这里插入图片描述

  • 然后使用命令,启动MQ服务
./activemq start

在这里插入图片描述

  • 启动消费者;
    在这里插入图片描述

  • 再看消息队列,发现消息已经没了!!!
    在这里插入图片描述


删除刚才的消息

案例(2): 设置持久化模式

在生产者中设定持久化模式.

producer.setDeliveryMode(DeliveryMode.PERSISTENT);

在这里插入图片描述

启动生产者,
在这里插入图片描述
在这里插入图片描述
然后模拟MQ服务器宕机:
先停机,再启动服务;
在这里插入图片描述

再去查看队列;待消费的消息还存在3条;
在这里插入图片描述
启动消费者;可接收到消息;
在这里插入图片描述
在这里插入图片描述


删除刚才的消息

案例(3):试试默认 是什么模式呢 ???

默认的是持久化模式哦;

启动默认的生产者,不设定持久化也不设定非持久化;
在这里插入图片描述

启动生产者,在这里插入图片描述
模拟MQ服务宕机;

在这里插入图片描述

查看队列,消息还在;
在这里插入图片描述
启动消费者;可成功消费
在这里插入图片描述
在这里插入图片描述


Topic 主题 持久化机制


注意: topic 默认是非持久化的, --> 生产消息时,消费者也要在线,那么消费者才能消费消息;
topic下的消息持久化,-- > 一旦消费者在MQ服务中注册,那么生产者成功发出的消息,这些消费者都可收到,不论这个MQ服务器是否宕机 / 消费者是否在线.

  • 注意要先运行消费者,也就是向MQ注册,表明自己订阅主题;
  • 运行生产者发送消息;
  • 无论消费者是否在线,都会收到消息,若不在线,那么下次连接时,就会将没有收到的消息接收.

案例1 持久化模式;

在生产者中设置开启持久化,

public class MyProduce {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String TOPIC_NAME = "myTopic";
    //
    public static void main(String[] args) throws JMSException {
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地; 主题
        Topic destination = session.createTopic(TOPIC_NAME);
        //5.创建消息生产者;
        MessageProducer producer = session.createProducer(destination);
        //设置持久化;
        producer.setDeliveryMode(DeliveryMode.PERSISTENT);
        connection.start();
        //生产消息存入到MQ中间件的主题中;
        for (int i = 1; i <= 3; i++) {
            //表名是第几条消息;
            TextMessage textMessage = session.createTextMessage("+this is the" + i + "message");
            producer.send(textMessage);
        }
        //关闭资源;
        producer.close();
        session.close();
        connection.close();
        System.out.println("+-+-+-+-+-+-+-+-+-+主题消息生成之后,发给中间大佬MQ了");
    }
}

消费者修改;

public class MyConsumer {
    //链接url
    public static final String ACTIVEMQ_URL = "tcp://192.168.59.128:61616";
    //目标队列名称;
    public static final String Topic_NAME = "myTopic";
    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是消费者LZQ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //1.按照自己的链接,创建连接的工厂;
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过工厂创建链接;-->开启;
        Connection connection = activeMQConnectionFactory.createConnection();

        //设置客户端的ID;
        connection.setClientID("myClientLZQ");
        //3.创建会话; 参数:  事务, 签收机制;
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地;--->主题
        Topic destination = session.createTopic(Topic_NAME);
        //主题描述;
        TopicSubscriber topicSubscriber = session.createDurableSubscriber(destination, "remarks---->");
        //启动;
        connection.start();
        //消息接收
        Message message = topicSubscriber.receive();
        while (null != message) {
            TextMessage textMessage = (TextMessage) message;
            System.out.println("收到的消息-->" + textMessage.getText());
            message = topicSubscriber.receive(1000L);
        }
        session.close();
        connection.close();
    }
}

先启动订阅,即启动消费者;
在这里插入图片描述

在这里插入图片描述
可看到描述;
在这里插入图片描述

此时运行持久化的生产者,进行消息发布;
在这里插入图片描述
OK,成功接收到消息;
在这里插入图片描述
在这里插入图片描述
那么此时订阅者[消费者]离线;
在这里插入图片描述


删除之前的消息;

案例2
先启动第一个订阅者LZQ
在这里插入图片描述
再启动订阅者Mark
在这里插入图片描述

在这里插入图片描述

启动生产者
在这里插入图片描述
在这里插入图片描述

第一位订阅者还未离线;
在这里插入图片描述


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小智RE0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值