初探Apache ActiveMQ

初探Apache ActiveMQ

一、前言

先来说一说消息队列,顾名思义,消息队列是按一定顺序存放消息的容器。为什么会有这种东西呢,我们来看这样一个场景:在微信出现之前,也就是短信还非常普及的时代,我们通过短信与他人进行沟通。平时没有什么,发送了的消息几乎马上会被对方。但是如果很多人给你发短信,举个非常极端的例子,大年三十晚上发短信祝福的时候,往往会很久很久才会收到信息。没有收到之前信息在哪里呢,自然是有一个消息队列装载这些消息。
另一方面,使用消息队列还有一个好处就是,可以将两个系统进行解耦,尤其对于高并发并且多个系统处理效率不一致的情况,使用消息队列可以增大系统的吞吐量和效率。

作为一个JAVA开发人员,比较熟悉的消息队列中间件就是Apache的ActiveMQ,下面对其进行介绍。

二、术语名词

名词英文全称缩写解释
生产者MessageProviderProvider产生或发送消息的系统
消费者MessageConsumerConsumer处理或接收消息的系统
点对点Point to PointP2P点对点消息模型
发布/订阅Publish/SubscribePub/Sub发布/订阅消息模型
连接工厂ConnectionFactory——创建消息连接的工厂类
连接Connection——用于接发消息的连接
会话Session——由Connection创建,一个接发消息的会话
消息目的地Destination——由Session创建,消息发送的目的地
队列Queue——用于点对点消息模型
主题Topic——用于发布/订阅模型

三、消息模型

所谓消息模型,就是消息发送和接收的模式。在ActiveMQ中,分为两种消息模式,点对点消息模型和发布/订阅消息模型。
点对点消息模型发布/订阅消息模型

点对点模型

点对点消息模型就是一个生产者产生的消息,最终会由一个消费者去消费。在ActiveMQ中,对于同一个Destination,发送的消息放在同一个Queue中,消费者在消息发布之前不需要进行监听,当消费者从消息队列获取消息时,会根据现有的Queue按照获取的优先级获取消息。而且可以配置何时确认Queue消息被消耗。

发布/订阅模型

发布/订阅模型则不是,一个生产者发出的消息可能被多个消费者消费。在ActiveMQ中,对于同一个Destination,会产生一个Topic,当生产者产线消息后,直接进行发布。消费者需要提前进行监听,消息发布后,会自动接收消息。如果消息发布的时候没有任何一个消费者监听,那这个消息将不会被任何消费者消费。

四、消息协议

息协议,是指发送消息的校验格式或者说是传输数据结构,ActiveMQ在官方的例子中,一共包含如下四种消息协议:

1. AMQP协议

Advanced Message Queuing Protocol,被设计为当前流行消息中间件的开放替代品,它的可靠性和互用性是使用它的两个原因。它提供了与消息传递相关的各种功能,包括可靠的排队,基于主题的发布和订阅消息传递,灵活的路由,事务和安全性等。

2. MQTT协议

Message Queue Telemetry Transport,最初是IBM的普遍计算技术团队和其工业部门的合伙人开发的,之后移入开源社区,并被移动应用中流行起来。

3. Openwire协议

这个协议是Apache自己的跨语言线路协议,来让不同的开发语言和平台使用ActiveMQ。

4. stomp协议

Simple/Streaming Text Oriented Messaging Protocol,是基于文本的消息协议,更加类似于HTTP。

五、Hello World

0. 开发准备

1. 启动服务

有两种启动方式:

  1. 通过命令行启动
    cd到下载ActiveMQ解压后的根路径,执行bin\activemq start
    命令行启动
    启动成功后,可以访问http://localhost:8161/admin/进入ActiveMQ监控系统。

  2. 嵌入式启动
    引入lib下的jar包,或者直接引入activemq-all.jar到项目中,既可通过如下代码进行嵌入式启动。

BrokerService broker = new BrokerService();
//在这里可以对broker进行相关的配置
broker.addConnector("tcp://localhost:61616");
broker.start();

2. 消息工作类

由于生产者和消费者的初始化,有很多的共通之处,因此编写工作父类

package org.openwire.test;

import java.io.Closeable;
import java.io.IOException;

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;

/**
 * 消息工作类
 * @author 银发Victorique
 * @date 20180316
 */
public class MyMessageWorker implements Closeable {

    //HOST
    private String host = "localhost";
    //端口
    private int port = 61616;
    //用户名
    private String username = "admin";
    //密码
    private String password = "password";
    //目的地
    private String destination = "sv_test";

    //连接工厂
    private ActiveMQConnectionFactory factory;
    //消息连接
    private Connection connection;
    //消息会话
    private Session session;
    //目的地
    private Destination dest;

    /**
     * 全默认配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @throws Exception
     */
    public MyMessageWorker(MessageType type, int acknowledgeType) throws JMSException {
        if(acknowledgeType != Session.AUTO_ACKNOWLEDGE && acknowledgeType != Session.CLIENT_ACKNOWLEDGE
                && acknowledgeType != Session.DUPS_OK_ACKNOWLEDGE && acknowledgeType != Session.SESSION_TRANSACTED) {
            throw new JMSException("消息确认类型不存在!");
        }
        if(type.equals(MessageType.Queue)) {
            this.dest = new ActiveMQQueue(destination);
        } else if(type.equals(MessageType.Topic)) {
            this.dest = new ActiveMQTopic(destination);
        } else {
            throw new JMSException("消息类型不存在!");
        }
        this.factory = new ActiveMQConnectionFactory("tcp://" + host + ":" + port);

        this.connection = factory.createConnection(username, password);
        connection.start();
        this.session = connection.createSession(false, acknowledgeType);
    }

    /**
     * 默认用户名密码的构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @throws Exception
     */
    public MyMessageWorker(MessageType type, int acknowledgeType, String host, int port, String destination) throws JMSException {
        this(type, acknowledgeType);
        this.host = host;
        this.port = port;
        this.destination = destination;
    }

    /**
     * 全配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @param username 用户名
     * @param password 密码
     * @throws Exception
     */
    public MyMessageWorker(MessageType type, int acknowledgeType, String host, int port, String destination, String username, String password) throws JMSException {
        this(type, acknowledgeType, host, port, destination);
        this.username = username;
        this.password = password;
    }

    @Override
    public void close() throws IOException {
        try {
            connection.close();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    // getter
    public Destination getDestination() {
        return dest;
    }

    public ActiveMQConnectionFactory getFactory() {
        return factory;
    }

    public Connection getConnection() {
        return connection;
    }

    public Session getSession() {
        return session;
    }

    /**
     * 消息类型
     * @author 银发Victorique
     * @date 20180316
     */
    public enum MessageType {
        Queue, Topic;
    }

}

3. 生产者

package org.openwire.test;

import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.TextMessage;

/**
 * 消息生产者
 * @author 银发Victorique
 * @date 20180316
 */
public class MyProvider extends MyMessageWorker {

    private MessageProducer producer;

    /**
     * 全默认配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @throws Exception
     */
    public MyProvider(MessageType type, int acknowledgeType) throws JMSException {
        super(type, acknowledgeType);
        this.producer = getSession().createProducer(getDestination());
    }

    /**
     * 默认用户名密码的构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @throws Exception
     */
    public MyProvider(MessageType type, int acknowledgeType, String host, int port, String destination) throws JMSException {
        super(type, acknowledgeType, host, port, destination);
    }

    /**
     * 全配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @param username 用户名
     * @param password 密码
     * @throws Exception
     */
    public MyProvider(MessageType type, int acknowledgeType, String host, int port, String destination, String username, String password) throws JMSException {
        super(type, acknowledgeType, host, port, destination, username, password);
    }

    /**
     * 发送消息
     * @param message 消息文本
     */
    public void send(String message) throws JMSException {
        TextMessage msg = getSession().createTextMessage(message);
        producer.send(msg);
    }
}

4. 消费者

package org.openwire.test;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.TextMessage;

/**
 * 消息消费者
 * @author SVictorique
 *
 */
public class MyConsumer extends MyMessageWorker {

    private MessageConsumer consumer;

    /**
     * 全默认配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @throws Exception
     */
    public MyConsumer(MessageType type, int acknowledgeType) throws JMSException {
        super(type, acknowledgeType);
        this.consumer = getSession().createConsumer(getDestination());
    }

    /**
     * 默认用户名密码的构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @throws Exception
     */
    public MyConsumer(MessageType type, int acknowledgeType, String host, int port, String destination) throws JMSException {
        super(type, acknowledgeType, host, port, destination);
    }

    /**
     * 全配置构造方法
     * @param type 消息类型 Topic或Queue
     * @param acknowledgeType 消息确认类型 1 2 3
     * @param host HOST地址
     * @param port 端口
     * @param destination 消息目的地
     * @param username 用户名
     * @param password 密码
     * @throws Exception
     */
    public MyConsumer(MessageType type, int acknowledgeType, String host, int port, String destination, String username, String password) throws JMSException {
        super(type, acknowledgeType, host, port, destination, username, password);
    }

    /**
     * 接收消息
     * @return
     * @throws JMSException
     */
    public String receive() throws JMSException {
        Message msg = consumer.receive();
        if( msg instanceof  TextMessage ) {
            return  ((TextMessage) msg).getText();
        } else {
            return null;
        }
    }

}

5. 测试类

package org.openwire.test;

import java.io.IOException;

import javax.jms.JMSException;
import javax.jms.Session;

import org.openwire.test.MyMessageWorker.MessageType;

public class App {

    public static void main(String[] args) throws Exception {
        int size = 10;
        for(int i=0; i<4; i++) {

            new Thread(new Runnable() {

                @Override
                public void run() {
                    //创建消费者
                    try(MyConsumer mc = new MyConsumer(MessageType.Queue, Session.AUTO_ACKNOWLEDGE);) {
                        String id = String.valueOf(Math.random()*100).substring(0, 1);
                        for(int i=0; i<size; i++) {
                            String msg = mc.receive();
                            System.out.println(id+"-ReceiveMessage: "+msg);
                        }
                    } catch (IOException | JMSException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        //创建生产者
        try(MyProvider mp = new MyProvider(MessageType.Queue, Session.AUTO_ACKNOWLEDGE);) {
            for(int i=0; i<size; i++) {
                String msg = "message"+i;
                System.out.println("SendMessage: "+msg);
                mp.send(msg);
            }
        }
    }

}

6. 测试结果

ActiveMQ测试结果

六、后记

本篇文章只是初步的介绍了一下Apache的ActiveMQ,并没有细致的介绍各个配置文件,只是让读者有一个初步的概念。当真实项目中需要使用的时候,能有备选方案,而不是书到用时方恨少。另外,ActiveMQ只是一个比较简单的消息队列中间件,还有很多高性能的消息队列,例如阿里巴巴的RocketMQ。最后,祝工作顺利。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值