消息中间件ActiveMQ学习笔记

目录

前言

让我们跟随尚硅谷周阳老师的讲解步入消息中间件MQ之ActiveMQ的学习吧。


1 入门概述

1.1 MQ的产品种类和对比

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 MQ的产生背景

在这里插入图片描述

1.2.1 系统之间接口耦合比较严重 ----解耦

在这里插入图片描述

1.2.2 面对大流量并发时,容易被冲垮 ----- 削峰

在这里插入图片描述

2.2.3 等待同步存在性能问题 ----- 异步

在这里插入图片描述
在这里插入图片描述

1.3 MQ的主要作用

在这里插入图片描述

1.4 MQ的定义

在这里插入图片描述

在这里插入图片描述

1.5 MQ的特点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 ActiveMQ 安装和控制台

2.1 ActiveMQ安装

前提条件:linux服务器要安装jdk8以上的版本。

在这里插入图片描述

跟随老师讲课版本是5.15.9 安装在linux上。 官网下载地址
安装步骤:

  1. /opt 目录下解压
  2. 将解压的文件夹 移动或剪切到 我们专门去创建的myactiveMQ文件夹下。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. 启动在这里插入图片描述
  4. 查看启动状态(三种方式)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. 关闭服务
    在这里插入图片描述
  6. 带日志方式的启动
    在这里插入图片描述

2.2 ActiveMQ控制台

首先将linux和windows的防火墙给关闭,这样才能进行相互连接。。因为我们将windows做客户端,将linux做服务器端,通过windows浏览器ip地址访问服务器端即可。

在这里插入图片描述
防火墙未关闭前:
在这里插入图片描述
防火墙关闭后:
在这里插入图片描述
注意linxu关闭防火墙服务的时候要区分是cnetos7还是centos6。
Linux关闭防火漆服务:
在这里插入图片描述
linux能连接到本机。
在这里插入图片描述
windows能ping通linux服务器
在这里插入图片描述

ActiveMQ端口号:
61616 :后台端口号
8161:前台端口号

在windows客户端中通过ip地址访问。如下图:(以前是localhost是因为安装就是在本地windows)

在这里插入图片描述
登录默认用户名和密码是:admin。
在这里插入图片描述
在这里插入图片描述

3 入门案例、MQ标准、API详解

3.1 pom导入依赖

 <dependencies>
        <!--activemq所需要的jar包配置-->
        <!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-all -->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.9</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.xbean/xbean-spring -->
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.16</version>
        </dependency>

		<!--整合spring的时候需要用到的依赖-->
        <!--下面是junit/log4j等基础通用配置-->
        <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.16.16</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.2 JMS 编码总体规范

在这里插入图片描述

在这里插入图片描述

3.3 Destination 简介

在这里插入图片描述
在这里插入图片描述

3.4 队列消息生产者的入门案例

public class JmsProduce {
   
    public static final String ACTIVEMQ_URL = "tcp://192.168.76.100:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException {
   
        //1.创建连接工程,按照给定的url地址,采用默认用户名和密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工场,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();

        //3.创建会话session
        //两个参数,第一个叫做事务/第二个叫做签收(boolean b,int i) AUTO_ACKNOWLEDGE 自动默认签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        //4.创建目的地(具体是队列还是主题topic)
         
        Queue queue = session.createQueue(QUEUE_NAME);

        //5. 创建消息的生产者
        MessageProducer messageProducer = session.createProducer(queue);
        //6.通过使用  MessageProducer  生产3条消息发送到mq队列里面
        for (int i = 1; i <= 3 ; i++) {
   
            //7.创建消息
            TextMessage textMessage = session.createTextMessage("msg ---" + i);  //理解为一个字符串
            //8.通过  messageProducer 发送给mq
            messageProducer.send(textMessage);
        }
        //9. 关闭资源   正着生产,倒着关闭
        messageProducer.close();
        session.close();
        connection.close();
        System.out.println("消息发送成功");
    }
}

Queue queue 直接alt+enter是前面这个queue类型,但是我们可以改成destination类型(queue是destination的子接口,功能更加强大)
destination的子接口有queue topic 父接口一般是定义规范。子接口一般是实现更加强大的功能
和集合接口我们常用arraylist接口一样 collection collection = new arrayList;
Destination destination = session.createQueue(QUEUE_NAME);
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.5 ActiveMQ控制台之队列

在这里插入图片描述
在这里插入图片描述

3.6 队列消费者的入门案例(同步阻塞/异步监听)

public class JmsConsumer {
   
        public static final String ACTIVEMQ_URL = "tcp://192.168.76.100:61616";
    	public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException, IOException {
   
        //1.创建连接工程,按照给定的url地址,采用默认用户名和密码
        //为什么要创建一个全局常量,是因为如果我们在创建对象的过程中直接写地址,那就是写死了,修改不方便。而创建一个全局常量,修改方便
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工场,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //3.创建会话session
        //两个参数,第一个叫做事务/第二个叫做签收(boolean b,int i)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //4.创建目的地(具体是队列还是主题topic)
        Queue queue = session.createQueue(QUEUE_NAME);
        //5. 创建消息的消费者
        MessageConsumer messageConsumer = session.createConsumer(queue);
        
      /**
       *
       * 同步阻塞方式(recieve)
       * 订阅者或接收者调用MessageConsumer的recieve()方法来接收消息,recieve方法在能够接收到消息之前(或超时之前)将一直阻塞。
       *
       *
       while(true){
            //因为生产者生产的是TextMessage这种类型的,所以消费者也要消费同种类型,所以要进行强制类型转换
            //recieve()里面没有任何参数的话,消费者会一直等着接收消息,即使当前消息已经消费完了。
            //recieve()设置时间,过时不候。
            TextMessage textMessage = (TextMessage) messageConsumer.receive(4000L);

            if(textMessage != null){
                System.out.println("消费者接收到消息:"+textMessage.getText());
            }else {
                break;
            }
        }
        //顺着申请,倒着关闭
        messageConsumer.close();
        session.close();
        connection.close();
       */


        /**
         * 通过监听的方式
         */

        messageConsumer.setMessageListener(new MessageListener() {
   
            @Override
            public void onMessage(Message message) {
   
                if(message != null && message instanceof TextMessage){
   
                    TextMessage textMessage = (TextMessage) message;
                    try {
   
                        System.out.println("消费者接收到消息:"+textMessage.getText());
                    } catch (JMSException e) {
   
                        e.printStackTrace();
                    }
                }
            }
        });
       
        //防止执行太快,保证控制台不灭
        //因为消费者进行连接到消息中间件,会有一系列验证,如果不写 System.in.read();这个,程序会马上执行完,但是消费者不会接收到任何消息
        System.in.read();
        messageConsumer.close();
        session.close();
        connection.close();
    }
}

注意看上面的代码,其实我们不难发现消息的消费者有两种模式。

第一种是同步阻塞式

  • 订阅者或接收者调用MessageConsumer的recieve()方法来接收消息,recieve方法在能够接收到消息之前(或超时之前)将一直阻塞

第二种是异步监听式

  • 我们可以使用MessageListener来监听消息,通过注册一个监听器,当有消息发送来时,系统自动调用MessageListener的onMessage方法处理消息。

从上面两种方式的区别来看,我们大多数都会使用异步监听式来处理。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.7 队列小结

3.7.1 队列特点

3.7.2 消费者情况

在这里插入图片描述
在这里插入图片描述

3.8 Topic 介绍

在这里插入图片描述
在这里插入图片描述

3.9 生产者 和消费者 案例

其实队列和主题这两种形式简单来说代码改动就在于session.createProducer(topic) / session.createTopic ,session.createProducer(queue) / session.createQueue
不信我们来看代码。

public class JmsProduce_Topic {
   
        public static final String ACTIVEMQ_URL = "tcp://192.168.76.100:61616";
   	    public static final String TOPIC_NAME = "topic-atguigu";

    public static void main(String[] args) throws JMSException {
   
        //1.创建连接工程,按照给定的url地址,采用默认用户名和密码
        //为什么要创建一个全局常量,是因为如果我们在创建对象的过程中直接写地址,那就是写死了,修改不方便。而创建一个全局常量,修改方便
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工场,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();

        //3.创建会话session
        //两个参数,第一个叫做事务/第二个叫做签收(boolean b,int i)AUTO_ACKNOWLEDGE 自动默认签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        //4.创建目的地(具体是队列还是主题topic)
        Topic topic = session.createTopic(TOPIC_NAME);

        //5. 创建消息的生产者
        MessageProducer messageProducer = session.createProducer(topic);
        //6.通过使用  MessageProducer  生产3条消息发送到mq队列里面
        for (int i = 1; i <= 3; i++) {
   
            //7.创建消息
            TextMessage textMessage = session.createTextMessage("TOPIC_NAME---" + i);  //理解为一个字符串
            //8.通过  messageProducer 发送给mq
            messageProducer.send(textMessage);
        }
        //9. 关闭资源   正着生产,倒着关闭
        messageProducer.close();
        session.close();
        connection.close();

        System.out.println("TOPIC-NAME消息发送到MQ完成");
    }
}
public class JmsConsumer_Topic {
   

	public static final String ACTIVEMQ_URL = "tcp://192.168.76.100:61616";
    public static final String TOPIC_NAME = "topic-atguigu";

    public static void main(String[] args) throws JMSException, IOException {
   
        //1.创建连接工程,按照给定的url地址,采用默认用户名和密码
        //为什么要创建一个全局常量,是因为如果我们在创建对象的过程中直接写地址,那就是写死了,修改不方便。而创建一个全局常量,修改方便
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        //2.通过连接工场,获得连接connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();

        //3.创建会话session
        //两个参数,第一个叫做事务/第二个叫做签收(boolean b,int i)
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        //4.创建目的地(具体是队列还是主题topic)
        Topic topic = session.createTopic(TOPIC_NAME);

        //5. 创建消息的消费者
        MessageConsumer messageConsumer = session.createConsumer(topic);

        /**
         * 通过监听的方式
         */
         
		//传统方式 匿名内部类方式
       /* messageConsumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if(message != null && message instanceof TextMessage){
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("消费者接收到topic消息:"+textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });*/

        //使用lambda表达式
        messageConsumer.setMessageListener((message -> {
   
            if(message != null && message instanceof TextMessage){
   
                TextMessage textMessage = (TextMessage) message;
                try {
   
                    System.out.println("消费者接收到topic消息:"+textMessage.getText());
                } catch (JMSException e) {
   
                    e.printStackTrace();
                }
            }
        }));
        //防止执行太快,保证控制台不灭
        //因为消费者进行连接到消息中间件,会有一系列验证,如果不写 System.in.read();这个,程序会马上执行完,但是消费者不会接收到任何消息
        System.in.read();
        messageConsumer.close();
        session.close();
        connection.close();

    }
}

在这里插入图片描述

3.10 topic和queue对比

在这里插入图片描述

4 JMS 规范

4.1 JMS 是什么

  1. JAVAEE是什么?
    在这里插入图片描述

  2. 什么是Java 消息服务?
    Java 消息服务指的是两个应用程序之间进行异步通信的API,它为标准协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持Java 应用程序开发。在JavaEE 中,当两个应用程序使用JMS 进行通信时,它们之间不是直接相连的,而是通过一个共同的消息收发服务组件关联起来以达到解耦/异步削峰的效果。
    在这里插入图片描述

  3. MQ几种落地产品对比:
    在这里插入图片描述

4.2 消息头

JMS 的消息头有哪些属性:

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

说明: 消息的生产者可以set 这些属性,消息的消费者可以get 这些属性。这些属性在send 方法里面也可以设置。

// 这里可以指定每个消息的目的地
textMessage.setJMSDestination(topic);
/*
持久模式和非持久模式。
一条持久性的消息:应该被传送“一次仅仅一次”,这就意味着如果JMS 提供者出现故障,该消息并不会丢失,它
会在服务器恢复之后再次传递。
一条非持久的消息:最多会传递一次,这意味着服务器出现故障,该消息将会永远丢失。
*/
textMessage.setJMSDeliveryMode(0);
/*
可以设置消息在一定时间后过期,默认是永不过期。
消息过期时间,等于Destination 的send 方法中的timeToLive 值加上发送时刻的GMT 时间值。
如果timeToLive 值等于0,则JMSExpiration 被设为0,表示该消息永不过期。
如果发送后,在消息过期时间之后还没有被发送到目的地,则该消息被清除。
*/
textMessage.setJMSExpiration(1000);
/* 消息优先级,从0-9 十个级别,0-4 是普通消息5-9 是加急消息。
JMS 不要求MQ 严格按照这十个优先级发送消息但必须保证加急消息要先于普通消息到达。默认是4 级。
*/
textMessage.setJMSPriority(10);
// 唯一标识每个消息的标识。MQ 会给我们默认生成一个,我们也可以自己指定。
textMessage.setJMSMessageID("ABCD");
// 上面有些属性在send 方法里也能设置
messageProducer.send(textMessage);

4.3 消息体

在这里插入图片描述
在日常开发和生产中我们最主要使用的就是TextMessage和MapMessage这两种形式,几乎涵盖了百分之九十五的情况。
在这里插入图片描述
在这里插入图片描述

4.4 消息属性

如果需要除消息头字段之外的值,那么可以使用消息属性。他是识别/去重/重点标注等操作,非常有用的方法。他们是以属性名和属性值对的形式制定的。可以将属性是为消息头得扩展,属性指定一些消息头没有包括的附加信息,比如可以在属性里指定消息选择器。消息的属性就像可以分配给一条消息的附加消息头一样。
它们允许开发者添加有关消息的不透明附加信息。它们还用于暴露消息选择器在消息过滤时使用的数据。
下图是设置消息属性的API:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.5 消息持久化

4.5.1 什么是持久化消息

在这里插入图片描述

4.5.2 queue持久化和非持久化

在这里插入图片描述
在这里插入图片描述

设置为非持久化:

在这里插入图片描述

  1. 启动生产者,查看消息:
    在这里插入图片描述
  2. 将mq宕机:
    在这里插入图片描述
  3. 重新启动
    在这里插入图片描述
  4. 再启动消费者,发现获取不到消息。消息丢失

在这里插入图片描述

  1. 查看情况,消息不见,丢失。
    在这里插入图片描述

持久化(默认情况下:)
重复和上面一样的操作
在这里插入图片描述
在这里插入图片描述
查看情况,消费者还是能消费到宕机前生产者生产的3条消息。
在这里插入图片描述

4.5.3 topic的持久化和非持久化

对于主题,我们都知道要先消费即订阅之后再生产消息,否则如果先生产消息,在订阅,原来生产的消息,会是废消息,就像微信公众号一样,你没有订阅某个公众号A,那么在你没订阅A之前,你是不会接收到这个A的生产的消息的。如果你订阅了A之后,那么A发布的消息就会被你接收到。所以我们得出结论,**对于主题而言,要先消费在生产。**所以对于主题而言,非持久化没有任何意

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值