ActiveMQ浅述

最近在看关于Spring源码深入解析的书,里面涉及到了MQ,之前在项目中都是和框架整合在一块或者有封装好的拿来用,这里单纯的介绍一下ActionMQ;

首先,什么是Active MQ?
Active MQ是Apache出品,最流行的,能力强劲的开源消息总线。这是百科给的官方介绍。具体点说,Active MQ,即Java消息服务(Java Message Service)应用程序接口,是一款面向消息的中间件API。中间件,顾名思义是用于在两个或者多个应用程序之间,或分布式系统中发送消息,进行异步通信。

那么既然是通信,都有什么通信模式呢
JMS具有两种通信模式:

      1、Point-to-Point Messaging Domain (点对点

      2、Publish/Subscribe Messaging Domain (发布/订阅模式

   在JMS API出现之前,大部分产品使用“点对点”和“发布/订阅”中的任一方式来进行消息通讯。JMS定义了这两种消息发送模型的规范,它们相互独立。任何JMS的提供者可以实现其中的一种或两种模型,这是它们自己的选择。JMS规范提供了通用接口保证我们基于JMS API编写的程序适用于任何一种模型。
     (1)、Point-to-Point Messaging Domain(点对点通信模型)
     这里写图片描述
注:点对点,顾名思义,是一个消息发送者对一个消息消费者(一对一关系);而且发送和消费没有时间上的限制,两者相对独立,互不影响;
 (2)、Publish/Subscribe Messaging Domain(发布/订阅通信模型)
 这里写图片描述
注:一个消息可以传递个多个订阅者(即:一个消息可以有多个接受方);
发布者与订阅者具有时间约束,针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态;
为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息;

关于消息通信,是异步还是同步呢
关于消费者消息同步/异步调度的问题:
在AMQ4时,代理(broker)同步或异步调度消息到消费者就是可配置的。现在我们也可以通过uri链接、connection 和 connectionFactory 的形式配置,这种方式的配置可以替代以前那种只能通过在broker端方式的配置。

无论同步或异步发送消息都是非常有意义的,因为针对消费比较快的消费者,我们使用同步(可以减少异步发送消息时产生的上下文切换),针对消费比较慢的消费者,我们使用异步。 同步发送消息的缺点是,对于生产者发送的消息,如果消费者消费的比较慢,那么生产者就会被阻塞。

默认配置是异步发送 (displayatchAsync=true),这种配置也保证了MQ的高性能。

那么Action MQ相对其他中间件技术有什么长足之处呢?
1. 多种语言和协议编写客户端。语言: Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议: OpenWire,Stomp REST,WSNotification,XMPP,AMQP
2. 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)
3. 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性
4. 通过了常见J2EE服务器(如 Geronimo,JBoss 4, GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上
5. 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
6. 支持通过JDBC和journal提供高速的消息持久化
7. 支持Ajax
8. 可以很容易得调用内嵌JMS provider,进行测试
9. ActiveMQ速度非常快;一般要比jbossMQ(内嵌在服务器中)快10倍。

那么Action MQ的应用场景有哪些呢
一般在应用开发中进行解耦和应用通信,当然ActiveMQ 中间件虽然用Java语言编写,但是ActiveMQ 也为C/C++、.NET、Perl、PHP、Python、Ruby 和一些其它语言提供客户端。在你考虑如何集成不同平台不同语言编写应用的时候,ActiveMQ 拥有巨大优势。此外,ActiveMQ 还提供交叉语言功能,该功能整合这种功能,无需使用远程过程调用(RPC)确实是个优势,因为消息协助应用解耦。

针对上面的问题,都有了解答。下面上练习的Demo代码:
首先,去官网http://activemq.apache.org/activemq-5153-release.html下载对应电脑系统的Active MQ版本;然后运行消息服务器即可。

(注:http://localhost:8161/admin/可进行观察;密码:admin;账户admin);
代码分为消息提供者/消费者两部分:

Pom.xml:

import org.apache.activemq.ActiveMQConnection;
import org.apache.`这里写代码片`activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * Created by MaiBenBen on 2018/4/21.
 */
public class JMSProduce {
    private static final Logger logger= LoggerFactory.getLogger(JMSProduce.class);
    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; // 默认连接
    private static final String PASSWORD=ActiveMQConnection.DEFAULT_PASSWORD;//默认密码
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; // 默认连接地址 为 failover://tcp://localhost:61616
    private static final int Num=100;
    public static void main(String[] args){
        ConnectionFactory connectionFactory;   //与JMS服务器连接工厂实例
        Connection connection=null;                //与JMS服务器的连接
        Session session;                       //连接中所产生的会话-发送、接受消息的线程
        Destination destination;               //消息的目的地-消息容器
        MessageProducer messageProducer;       //消息生产者
        //ActiveMQConnectionFactory实例化连接工厂对象
        connectionFactory=new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKER_URL) ;
        try{
            connection=connectionFactory.createConnection();
            connection.start();
            session=connection.createSession(true,Session.AUTO_ACKNOWLEDGE); //提交任务,自动确认
//            destination=session.createQueue("MQFirstQueue");//创建消息队列
            destination = session.createTopic("MQThirdTopic"); // 创建主题
            messageProducer=session.createProducer(destination);
            sendMessage(session,messageProducer);
            session.commit();
        }catch (JMSException e) {
            e.printStackTrace();
        }finally {
            if(connection!=null){                    //这是connection初始化为null的原因
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void sendMessage(Session session, MessageProducer messageProducer) {
        for(int i=0;i<Num;i++){
            try {
                TextMessage textMessage=session.createTextMessage("ActiveMQ 发送的消息 "+i);
                messageProducer.send(textMessage);
                logger.info("ActiveMQ发送消息是:MQ:{}",i);
            } catch (JMSException e) {
                logger.error("ActiveMQ发送消息异常:MQ:{}",i);
                e.printStackTrace();
            }
        }
    }
}

Consumer1:

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * Created by MaiBenBen on 2018/4/21.
 */
public class JMSConsumer {

    private static final Logger logger= LoggerFactory.getLogger(JMSConsumer.class);
    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; // 默认连接
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; // 默认密码
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; // 默认连接地址 为 failover://tcp://localhost:61616

    public static void main(String args[]) {
        ConnectionFactory connectionFactory;   //与JMS服务器连接工厂实例
        Connection connection=null;                //与JMS服务器的连接
        Session session;                       //连接中所产生的会话-发送、接受消息的线程
        Destination destination;               //消息的目的地-消息容器
        MessageConsumer messageConsumer; // 消息消费者

        // 实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 消费消息不需要事务,自动确认
//            destination = session.createQueue("MQFirstQueue"); // 创建消息队列
            destination = session.createTopic("MQThirdTopic");//创建消息订阅者
            messageConsumer = session.createConsumer(destination); // 创建消息消费者

//循环消费消息
//            while (true) {
//                TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);// 设置延时为100s
//                if (textMessage!=null) { // 接收到消息
//                    logger.info("消费消息:MQ:{}",textMessage.getText());
//                }else {
//                    break;
//                }
//            }
            messageConsumer.setMessageListener(new Listener());
        } catch (JMSException e) {
            logger.error("消费消息异常!");
            e.printStackTrace();
        }
    }

}

Consumer2:

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * Created by MaiBenBen on 2018/4/21.
 */
public class JMSConsumerSecond {
    private static final Logger logger= LoggerFactory.getLogger(JMSConsumer.class);
    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER; // 默认连接
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; // 默认密码
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; // 默认连接地址 为 failover://tcp://localhost:61616

    public static void main(String args[]) {
        ConnectionFactory connectionFactory;   //与JMS服务器连接工厂实例
        Connection connection=null;                //与JMS服务器的连接
        Session session;                       //连接中所产生的会话-发送、接受消息的线程
        Destination destination;               //消息的目的地-消息容器
        MessageConsumer messageConsumer; // 消息消费者

        // 实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 消费消息不需要事务,自动确认
//            destination = session.createQueue("MQFirstQueue"); // 创建消息队列
            destination = session.createTopic("MQThirdTopic");//创建消息订阅者
            messageConsumer = session.createConsumer(destination); // 创建消息消费者

//循环消费消息
//            while (true) {
//                TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);// 设置延时为100s
//                if (textMessage!=null) { // 接收到消息
//                    logger.info("消费消息:MQ:{}",textMessage.getText());
//                }else {
//                    break;
//                }
//            }
            messageConsumer.setMessageListener(new ListenerSecond());
        } catch (JMSException e) {
            logger.error("消费消息异常!");
            e.printStackTrace();
        }
    }

}

Listener1:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * Created by MaiBenBen on 2018/4/21.
 */
public class Listener implements MessageListener {
    private static final Logger logger= LoggerFactory.getLogger(Listener.class);
    @Override
    public void onMessage(Message message) {
        try {
//            logger.info("消费消息:MQ:{}",((TextMessage)message).getText());
            logger.info("订阅者一消费消息:MQ:{}",((TextMessage)message).getText());
        } catch (JMSException e) {
            e.printStackTrace();
            logger.info("消费消息异常!");
        }
    }
}

Listener2:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * Created by MaiBenBen on 2018/4/21.
 */
public class ListenerSecond implements MessageListener{
    private static final Logger logger= LoggerFactory.getLogger(Listener.class);
    @Override
    public void onMessage(Message message) {
        try {
//            logger.info("消费消息:MQ:{}",((TextMessage)message).getText());
            logger.info("订阅者二消费消息:MQ:{}",((TextMessage)message).getText());
        } catch (JMSException e) {
            e.printStackTrace();
            logger.info("消费消息异常!");
        }
    }
}

点对点演示结果:
这里写图片描述
订阅者/消费者演示结果:
这里写图片描述
上述代码分别演示了点对点、订阅者/消费者两种模式,注意:在订阅者/消费者中,需要先进行订阅活动,才能进行发布操作;针对消费消息的情况,写了循环和监听两种形式。这里的代码像创建ConnectionFactory\Connection\Session\MessageProduce\Destination等是十分冗余的,在Spring中完全可以通过配置文件进行完成。但如果你看到了Spring中的JmsTemplate、和DefaultMessageListenerContainer等监听器的源码时,你会发现源码中的干货代码也就是上面纯代码中的部分,只是添加了部分辅助代码和不同情况、异同的策略而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魏小言

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

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

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

打赏作者

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

抵扣说明:

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

余额充值