ActiveMQ入门案例

工程建立

我们来新建一个工程,然后导入相关依赖
在这里插入图片描述
修改pom.xml导入activeMQ依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chaytech</groupId>
    <artifactId>activemq_examples</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--activemq需要的jav包-->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.16</version>
        </dependency>
        <!--下面是junit/log4等通用配置-->
        <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.18</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <modules>
        <module>activemq_demo</module>
    </modules>
</project>

JMS概述

编码前,我们先来了解一下JMS规范,什么是JMS呢?JMS(Java Message Service)是Java消息服务的规范,任何消息中间件都需要遵守这种规范,套路都是一样的,例如传统的JDBC开发
在这里插入图片描述
JMS开发的基本步骤:
在这里插入图片描述

Destination简介

Destination中文翻译就是目的地的意思,简单理解就是我们消息的接收方。
Destination分为两种模式:

  • 队列(queue)
  • 主题(topic)

在点对点的消息传递域中,目的地被称为队列(queue),点对点消息传递特点如下:

  1. 每个消息只能有一个消费者,类似于1对1的关系。好比个人快递自己领自己的。
  2. 消息的生产者和消费者之间没有时间上的相关性。无论消费者在生产者发送消息的时候是否处于运行状态,消费者都可以提取消息。好比我们的发送短信,发送者发送后不见得接收者会即收即看。
  3. 消息被消费后队列中不会再存储,所以消费者不会消费到已经被消费掉的消息。
    在这里插入图片描述

在发布订阅消息传递域中,目的地被称为主题(topic),发布/订阅消息传递域的特点如下:

  1. 生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N的关系;
  2. 生产者和消费者之间有时间上的相关性。订阅某一个主题的消费者只能消费自它订阅之后发布的消息。
  3. 生产者生产时,topic不保存消息它是无状态的不落地,假如无人订阅就去生产,那就是一条废消息,所以,一般先启动消费者再启动生产者。
    在这里插入图片描述

JMS规范允许客户创建持久订阅,这在一定程度上放松了时间上的相关性要求。持久订阅允许消费者消费它在未处于激活状态时发送的消息。一句话,好比我们的微信公众号订阅

队列消息(Queue)入门案例

队列消息生产者:

package com.chaytech.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息生产者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/21 22:20
 */
public class JmsProducer {

    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);
            // 创建消息的生产者并指定队列
            messageProducer = session.createProducer(queue);
            // 通过使用消息生产者,生产消息,发送到MQ的队列里面
            for (int i = 1; i <= 3; i++) {
                TextMessage textMessage = session.createTextMessage("hello_activeMQ_Server" + i);
                messageProducer.send(textMessage);
            }
            System.out.println("**********msg send sucess**********");
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                messageProducer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

运行消息生产者代码,然后打开activeMQ控制台,点击Queues
在这里插入图片描述
Number Of Pending Messages:等待消费的消息,这个是未出队列的数量,公式=总接收数-总出队列数。
Number Of Consumers:消费者数量,消费者端的消费者数量。
Messages Enqueued:进队消息数,进队列的总消息量,包括出队列的。这个数只增不减。
Messages Dequeued:出队消息数,可以理解为是消费者消费掉的数量。

当有一个消息进入这个队列时,等待消费的消息是1,进入队列的消息是1。
当消息消费后,等待消费的消息是0,进入队列的消息是1,出队列的消息是1。
当再来一条消息时,等待消费的消息是1,进入队列的消息就是2。

前面我们讲了怎么来生产消息,下面我们来说一下如何来消费消息。

同步阻塞式消费(receive):

package com.chaytech.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息消费者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/22 10:45
 */
public class JmsConsumer {
    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);
            // 创建消息消费者并指定消费的队列
            messageConsumer = session.createConsumer(queue);
            while (true) {
                /**
                 * reveive() 一直等待接收消息,在能够接收到消息之前将一直阻塞。 是同步阻塞方式 。和socket的accept方法类似的。
                 * reveive(Long time) : 等待n毫秒之后还没有收到消息,就是结束阻塞。
                 * 因为发送消息的时候是TextMessage类型,所以消费消息的时候也要是TextMessage类型,要以一一对应
                 */
                TextMessage textMessage = (TextMessage) messageConsumer.receive();
                if(textMessage == null){
                    break;
                }
                System.out.println("消费掉的消息 -> " + textMessage.getText());
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                messageConsumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

异步监听非阻塞方式消费(MessageListener ):

package com.chaytech.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息消费者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/22 10:45
 */
public class JmsConsumer {
    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String QUEUE_NAME = "test_queue_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建队列
            Queue queue = session.createQueue(QUEUE_NAME);
            // 创建消息消费者并指定消费的队列
            messageConsumer = session.createConsumer(queue);
            
            /**
             * 通过监听的方式来消费消息,是以异步非阻塞的方式来消费消息
             * 通过messageConsumer 的setMessageListener 注册一个监听器,当有消息发送来时,系统自动调用MessageListener 的 onMessage 方法处理消息
             */
             messageConsumer.setMessageListener((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();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                messageConsumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

前面我们只使用了一个消费者,那如果有多个消费者呢?情况会怎么样,下面就是通过实验得到的结论:

  1. 当只有一个消费者的时候,此消费者会消费掉所有的消息
  2. 当有两个消费者A和B的时候,A先启动、B后启动,A会消费掉所有的消息,B将消费不到消息
  3. 当生产者发布消息之前,已经有多个消费者启动就绪了,正等待消费消息,此时生产者发布多条消息,那么activeMQ会根据消费者的数量,轮询来消费消息,例如A消费一条,B消费一条,轮询下去,直到没有消息可消费

主题消息(Topic)入门案例

主题消息与前面所讲的队列消息代码基本一致,唯一的区别就是将创建队列(createQueue)修改为创建主题(createTopic);

消息生产者:

package com.chaytech.activemq.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息生产者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/21 22:20
 */
public class JmsProducer {

    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String TOPIC_NAME = "test_topic_1"; // 消息主题名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建主题
            Topic topic = session.createTopic(TOPIC_NAME);
            // 创建消息的生产者并指定队列
            messageProducer = session.createProducer(topic);
            // 通过使用消息生产者,生产消息,发送到MQ的队列里面
            for (int i = 1; i <= 3; i++) {
                TextMessage textMessage = session.createTextMessage("topic_message" + i);
                messageProducer.send(textMessage);
            }

            System.out.println("**********topic msg send sucess**********");
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                messageProducer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

消息消费者:

package com.chaytech.activemq.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息消费者
 *
 * @author Chency
 * @email [email protected]
 * @Date 2020/03/22 10:45
 */
public class JmsConsumer {
    private static final String BROKER_URL = "tcp://192.168.0.166:61616"; // activeMQ服务地址
    private static final String TOPIC_NAME = "test_topic_1"; // 消息队列名称

    public static void main(String[] args) {
        /**
         * 根据我们指定activeMQ服务地址来创建activeMQ工厂
         * 如果我们自己不指定activeMQ服务地址则默认为本机
         */
        ActiveMQConnectionFactory activeMQConnection = new ActiveMQConnectionFactory(BROKER_URL);
        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;
        try {
            // 通过连接工厂,获得connection
            connection = activeMQConnection.createConnection();
            //  打开连接
            connection.start();
            /**
             * 创建session,得到会话
             * 两个参数transacted=事务,acknowledgeMode=确认模式(签收)
             */
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // 根据session创建主题
            Topic topic = session.createTopic(TOPIC_NAME);
            // 创建消息消费者并指定消费的队列
            messageConsumer = session.createConsumer(topic);

            /**
             * 阻塞式消息消费
             * reveive() 一直等待接收消息,在能够接收到消息之前将一直阻塞。 是同步阻塞方式 。和socket的accept方法类似的。
             * reveive(Long time) : 等待n毫秒之后还没有收到消息,就是结束阻塞。
             * 因为发送消息的时候是TextMessage类型,所以消费消息的时候也要是TextMessage类型,要以一一对应
             */
           /* while (true) {
                TextMessage textMessage = (TextMessage) messageConsumer.receive();
                if(textMessage == null){
                    break;
                }
                System.out.println("消费掉的消息 -> " + textMessage.getText());
            }*/

            /**
             * 通过监听的方式来消费消息,是以异步非阻塞的方式来消费消息
             * 通过messageConsumer 的setMessageListener 注册一个监听器,当有消息发送来时,系统自动调用MessageListener 的 onMessage 方法处理消息
             */
            messageConsumer.setMessageListener((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();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                messageConsumer.close();
                session.close();
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

运行消息生产者代码,然后打开activeMQ控制台,点击Topics
在这里插入图片描述
Topic有多个消费者时,消费消息总数 = 消费者数量*生产消息总数;
当多个消费者订阅同一个主题时,每个消费者都能消费此主题下同等数量的消息;
主题消息只有当你订阅了此主题,你才能消费此消息,如果先生产消息,后订阅消息,那么将消费不到订阅前的消息,只能消费订阅后消息,例如微信公众号,如果你不订阅此公众号,那么你将收不到此公众号推送的消息,只有订阅之后才能收到;

队列消息(Queue)和主题消息(Topic)对比

对比项目 queue topic
工作模式 负载均衡模式,如果当前没有消费者,消息也不会丢弃,如果有多个消费者,一条消息只能被其中一个消费者消费,并且可以有ack信息 订阅模式,如果当前没有订阅者,那么此消息将会被丢弃,如果有多个订阅者,那么这些订阅者都能够消费此消息
有无状态 queue数据默认会在mq服务器上以文件形式持久化,activeMQ默认保存在安装目录下的/data/kahadb目录下面,也可配置为数据库存储 无状态
传递完整性 消息不会丢弃 如果没有订阅者,消息会被丢弃
处理效率 由于一条消息只能被一个消费者消费,所以消费者再多,性能也不会受到太大的影响,当然不同的消息协议的处理效率也是有差异的 由于消息被消费的数量是由订阅者的数量决定的,所以随着订阅者数量的增加性能会出现明显的降低,并且还有不同消息协议所带来的性能影响
发布了107 篇原创文章 · 获赞 19 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/chen_changying/article/details/105028192