ActiveMQ入门教程

0x01、ActiveMQ简介

1、什么是ActiveMQ?

Apache ActiveMQ ™ is the most popular and powerful open source messaging and Integration Patterns server.

ActiveMQ是一个MOM,是一个实现了JMS规范的系统间远程通信的消息代理。

2、什么是MOM?

MOM是面向消息中间件(Message-oriented middleware),是用于以分布式应用或系统中的异步、松耦合、可靠、可扩展和安全通信的一类软件。MOM的总体思想是它作为消息发送器和消息接受器之间的消息中介,这种中介提供了一个全新的水平松耦合。

3、ActiveMQ中一些基本概念

  • Provider :纯Java语言编写的JMS接口实现(比如ActiveMQ)
  • Domains:消息传递方式,包括点对点(P2P)、发布订阅(Pub/Sub)两种。
  • Connection factory:客户端使用连接工厂来创建与JMS provider的连接
  • Destination:消息被寻址、发送以及接受的对象

4、P2P和Pub/Sub的区别

1)、P2P

P2P消息域使用queue作为Destination,消息可以被同步和一部的发送和接收,每个消息只会给一个Consumer传递一次。

Consumer可以使用MessageConsumer.receive()同步接收消息;也可以使用Message.SetMessageListener()注册一个监听器来实现一部接收,在后面的Coding环节中会有代码演示。

多个Consumer可以注册到同一个Queue,但一个消息只能被一个Consumer所接收,然后由该Consumer来确认消息。并且这种情况下,Provider对所有注册的Consumer以轮询的方式发送消息。

2)、Pub/Sub

发布订阅模式的消息域使用topic作为Destination,发布者向topic发送消息,订阅者注册接收来自topic的消息。发送到topic的任何消息都将自动传递给所有订阅者。接收方式有同步和异步与P2P相同。

除非显示指定,否者topic不会为订阅者保留消息。当然可以通过持久化订阅来实现消息的保存。这种情况,当订阅者与发布者断开时,发布者会为他存储消息。当持久化订阅者重新连接时,将会收到所有的断连期间未收到的消息。

5、使用ActiveMQ应用大致步骤

  • 获取连接工厂
  • 使用连接工厂创建连接
  • 启动连接
  • 从连接创建会话
  • 获取 Destination
  • 创建 Producer,或
    • 创建 Producer
    • 创建 message
  • 创建 Consumer,或发送或接收message发送或接收 message
    • 创建 Consumer
    • 注册消息监听器(可选)
  • 发送或接收 message
  • 关闭资源(connection, session, producer, consumer 等)

0x2、ActiveMQ的存储机制

1)、KahaDB

ActiveMQ 5.3 版本起的默认存储方式。KahaDB存储是一个基于文件的快速存储消息,设计目标是易于使用且尽可能快。它使用基于文件的消息数据库意味着没有第三方数据库的先决条件。

要启用 KahaDB 存储,需要在 activemq.xml 中进行以下配置:

<broker brokerName="broker" persistent="true" useShutdownHook="false">
        <persistenceAdapter>
                <kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="16mb"/>
        </persistenceAdapter>
</broker>

2)、AMQ

与 KahaDB 存储一样,AMQ存储使用户能够快速启动和运行,因为它不依赖于第三方数据库。AMQ 消息存储库是可靠持久性和高性能索引的事务日志组合,当消息吞吐量是应用程序的主要需求时,该存储是最佳选择。但因为它为每个索引使用两个分开的文件,并且每个 Destination 都有一个索引,所以当你打算在代理中使用数千个队列的时候,不应该使用它。

<persistenceAdapter>
        <amqPersistenceAdapter
                directory="${activemq.data}/kahadb"
                syncOnWrite="true"
                indexPageSize="16kb"
                indexMaxBinSize="100"
                maxFileLength="10mb" />
</persistenceAdapter>

3)、JDBC

选择关系型数据库,通常的原因是企业已经具备了管理关系型数据的专长,但是它在性能上绝对不优于上述消息存储实现。事实是,许多企业使用关系数据库作为存储,是因为他们更愿意充分利用这些数据库资源。

<beans>
        <broker brokerName="test-broker" persistent="true" xmlns="http://activemq.apache.org/schema/core">
                <persistenceAdapter>
                        <jdbcPersistenceAdapter dataSource="#mysql-ds"/>
                </persistenceAdapter>
        </broker>
        <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
                <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/>
                <property name="username" value="activemq"/>
                <property name="password" value="activemq"/>
                <property name="maxActive" value="200"/>
                <property name="poolPreparedStatements" value="true"/>
        </bean>
</beans>

4)、内存存储

内存消息存储器将所有持久消息保存在内存中。在仅存储有限数量 Message 的情况下,内存消息存储会很有用,因为 Message 通常会被快速消耗。在 activema.xml 中将 broker 元素上的 persistent 属性设置为 false 即可。

<broker brokerName="test-broker" persistent="false" xmlns="http://activemq.apache.org/schema/core">
        <transportConnectors>
                <transportConnector uri="tcp://localhost:61635"/>
        </transportConnectors>
</broker>

0x03、ActiveMQ接收和发送消息

0、添加Maven依赖

 <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-all</artifactId>
      <version>5.14.5</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-spring -->
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-spring</artifactId>
      <version>5.14.5</version>
    </dependency>

1、基于JMS方式发送和接收消息

Sender.java

package com.durian.jms;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class MessageSender {

    //发送次数
    public static final int SEND_NUM = 5;
    //tcp地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    //目标,在ActiveMQ管理员控制台创建 http://localhost:8161/admin/queues.jsp
    public static final String DESTINATION = "durian.mq.queue";

    public static void sendMessage(Session session, MessageProducer producer) throws Exception {
        for (int i = 0; i < SEND_NUM; i++) {
            String message = "发送消息第" + (i + 1) + "条";
            TextMessage text = session.createTextMessage(message);

            System.out.println(message);
            producer.send(text);
        }
    }

    public static void run() throws Exception {
        Connection connection = null;
        Session session = null;
        try {
            // 创建链接工厂
            ConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, BROKER_URL);
            // 通过工厂创建一个连接
            connection = factory.createConnection();
            // 启动连接
            connection.start();
            // 创建一个session会话
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            // 创建一个消息队列
            Destination destination = session.createQueue(DESTINATION);
            // 创建消息制作者
            MessageProducer producer = session.createProducer(destination);
            // 设置持久化模式
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            sendMessage(session, producer);
            // 提交会话
            session.commit();

        } catch (Exception e) {
            throw e;
        } finally {
            // 关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        MessageSender.run();
    }

}

Receiver.java

package com.durian.jms;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;


public class MessageReceiver {
    // tcp 地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    // 目标,在ActiveMQ管理员控制台创建 http://localhost:8161/admin/queues.jsp
    public static final String DESTINATION = "durian.mq.queue";

    public static void run() throws Exception {

        Connection connection = null;
        Session session = null;
        try {
            // 创建链接工厂
            ConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection
                    .DEFAULT_PASSWORD, BROKER_URL);
            // 通过工厂创建一个连接
            connection = factory.createConnection();
            // 启动连接
            connection.start();
            // 创建一个session会话
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            // 创建一个消息队列
            Destination destination = session.createQueue(DESTINATION);
            // 创建消息制作者
            MessageConsumer consumer = session.createConsumer(destination);

            while (true) {
                // 接收数据的时间(等待) 100 ms
                Message message = consumer.receive(1000 * 100);

                TextMessage text = (TextMessage) message;
                if (text != null) {
                    System.out.println("接收:" + text.getText());
                } else {
                    break;
                }
            }

            // 提交会话
            session.commit();

        } catch (Exception e) {
            throw e;
        } finally {
            // 关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        MessageReceiver.run();
    }
}

2、Queue队列点对点发送接收消息

package com.durian.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class QueueSender {
    //发送次数
    public static final int SEND_NUM = 5;
    // tcp 地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    //目标队列
    public static final String DESTINATION = "durian.mq.queue";

    public static void run() throws Exception{
        QueueConnection connection = null;
        QueueSession session = null;
        try {
            //创建连接工厂
            QueueConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER,
                        ActiveMQConnectionFactory.DEFAULT_PASSWORD,BROKER_URL);
            //通过工厂创建连接
            connection = factory.createQueueConnection();
            //启动连接
            connection.start();
            //创建一个session会话
            session = connection.createQueueSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //创建过一个消息队列
            Queue queue = session.createQueue(DESTINATION);
            //创建消息发送者
            javax.jms.QueueSender sender = session.createSender(queue);
            //设置持久化模式
            sender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            sendMessage(session, sender);
            //提交会话
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            //关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }


    public static void sendMessage(QueueSession session, javax.jms.QueueSender sender) throws Exception {
        for (int i = 0; i < SEND_NUM; i++) {
            String message = "发送的消息" + i;
            MapMessage mapMessage = session.createMapMessage();
            mapMessage.setString("text", message);
            mapMessage.setLong("time", System.currentTimeMillis());
            System.out.println(mapMessage);
            sender.send(mapMessage);
        }
    }


    public static void main(String[] args) throws Exception {
        QueueSender.run();
    }

}


package com.durian.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class QueueReceiver {
    // tcp 地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    //目标队列
    public static final String DESTINATION = "durian.mq.queue";

    public static void run() throws Exception {
        QueueConnection connection = null;
        QueueSession session = null;
        try {
            //创建连接工厂
            QueueConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,BROKER_URL);
            //通过工厂创建连接
            connection = factory.createQueueConnection();
            //启动连接
            connection.start();
            //创建一个session会话
            session = connection.createQueueSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //创建过一个消息队列
            Queue queue = session.createQueue(DESTINATION);
            //创建消息接受者
            javax.jms.QueueReceiver receiver = session.createReceiver(queue);

            //设置消息监听器,实现一部监听
            receiver.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    if (message != null) {
                        MapMessage map = (MapMessage) message;
                        try {
                            System.out.println(map.getLong("time") + "接受#" + map.getString("text"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            //休眠100ms再关闭
            Thread.sleep(100);
            //提交会话
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            //关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        QueueReceiver.run();
    }
}

3、Topic主题发布和订阅消息模式

package com.durian.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class TopicSender {
    //发送次数
    public static final int SEND_NUM = 5;
    // tcp 地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    //目标队列
    public static final String DESTINATION = "durian.mq.queue";

    public static void run() throws Exception{
        TopicConnection connection = null;
        TopicSession session = null;
        try {
            //创建连接工厂
            TopicConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,BROKER_URL);
            //通过工厂创建连接
            connection = factory.createTopicConnection();
            //启动连接
            connection.start();
            //创建一个session会话
            session = connection.createTopicSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //创建过一个消息队列
            Topic topic = session.createTopic(DESTINATION);
            //创建消息发送者
            TopicPublisher publisher = session.createPublisher(topic);
            //设置持久化模式
            publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            sendMessage(session, publisher);
            //提交会话
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            //关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }


    public static void sendMessage(TopicSession session, TopicPublisher publisher) throws Exception {
        for (int i = 0; i < SEND_NUM; i++) {
            String message = "发送的消息:" + i;
            MapMessage mapMessage = session.createMapMessage();
            mapMessage.setString("text", message);
            mapMessage.setLong("time", System.currentTimeMillis());
            System.out.println(mapMessage);
            publisher.send(mapMessage);
        }
    }


    public static void main(String[] args) throws Exception {
        TopicSender.run();
    }
}


package com.durian.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class TopicReceiver {
    // tcp 地址
    public static final String BROKER_URL = "tcp://localhost:61616";
    //目标队列
    public static final String DESTINATION = "durian.mq.queue";

    public static void run() throws Exception{
        TopicConnection connection = null;
        TopicSession session = null;
        try {
            //创建连接工厂
            TopicConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,BROKER_URL);
            //通过工厂创建连接
            connection = factory.createTopicConnection();
            //启动连接
            connection.start();
            //创建一个session会话
            session = connection.createTopicSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
            //创建过一个消息队列
            Topic topic = session.createTopic(DESTINATION);
            //创建消息订阅者
            TopicSubscriber subscriber = session.createSubscriber(topic);
            subscriber.setMessageListener(new MessageListener() {
                public void onMessage(Message message) {
                    if (message != null) {
                        MapMessage map = (MapMessage) message;
                        try {
                            System.out.println(map.getLong("time") + ",接受#" + map.getString("text"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            //休眠100ms
            Thread.sleep(100);
            //提交会话
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            //关闭释放资源
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }



    public static void main(String[] args) throws Exception {
        TopicReceiver.run();
    }
}

4、ActiveMQ与Spring整合

package com.durian.spring;

import org.apache.xbean.spring.context.FileSystemXmlApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
import java.util.Date;

public class SpringSender {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring.xml");
        JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");

        jmsTemplate.send(new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setString("message", "current system time:" + new Date());
                return mapMessage;
            }
        });
    }
}


package com.durian.spring;

import org.apache.xbean.spring.context.FileSystemXmlApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.jms.core.JmsTemplate;

import java.util.Map;

public class SpringReceiver {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring.xml");
        JmsTemplate jmsTemplate = (JmsTemplate) context.getBean("jmsTemplate");

        while (true) {
            Map<String, Object> map = (Map<String, Object>) jmsTemplate.receiveAndConvert();
            System.out.println("接受到的消息:" + map.get("message"));
        }
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <!-- 连接池  -->
    <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
        <property name="connectionFactory">
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL" value="tcp://localhost:61616"/>
            </bean>
        </property>
    </bean>

    <!-- 连接工厂 -->
    <bean id="activeMQConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616"/>
    </bean>

    <!-- 配置消息目标 -->
    <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
        <!-- 目标,在ActiveMQ管理员控制台创建 http://localhost:8161/admin/queues.jsp -->
        <constructor-arg index="0" value="durian.mq.queue"/>
    </bean>

    <!-- 消息模板 -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="activeMQConnectionFactory"/>
        <property name="defaultDestination" ref="destination"/>
        <property name="messageConverter">
            <bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
        </property>
    </bean>
</beans>

参考链接:

posted on 2018-08-05 13:06  DurianCoder  阅读(258)  评论(0)    收藏  举报