ActiveMQ基础入门笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_31156277/article/details/80993805

ActiveMQ入门

1.1 JMS

JMSJava消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API(只是接口,没有具体实现),用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。实现JMS接口的消息中间件称为JMS Provider


常用的有ActiveMQ(基本满足大多数应用场景,小规模场景还不错),RabbitMQ(性能不错,数据高可靠,支持集群),kafka (性能、吞吐量都非常高,通过replicate方式保证高可用,可能出现少量数据丢失)。ActiveMQ是Apache下的开源项目,完全支持JMS1.1和J2EE1.4规范的JMS Provider实现


面向消息的中间件。 发送者将信息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接受者。这种模式下,发送和接收是异步的,发送者无需等待;二者的生命周期未必相同;一对多通信:对于一个消息可以有多个接受者。


1.2 JMS相关术语

名称 描述
Provider(MessageProvider) 生产者
Consumer(MessageConsumer) 消费者
PTP(Point to Point) 即点对点的消息模型
Pub/Sub Publish/Subscribe,即发布/订阅的消息模型
Queue 队列目标
Topic 主题目标
ConnectionFactory 连接工厂,JMS用它创建连接
Connection JMS客户端到JMS Provider的链接
Destination 消息的目的地
Session 会话,一个发送或接受消息的线程

1.2.1 JSM消息格式

JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性

  • StreamMessage Java原始值的数据流
  • MapMessage 一套名称-值对
  • TextMessage 一个字符串对象
  • ObjectMessage 一个序列化的 Java对象
  • BytesMessage 一个字节的数据流

二、ActiveMQ入门案例

2.1 下载安装

  • http://activemq.apache.org/
  • 运行bin 目录下的 activemq.bat
  • 输入 http://127.0.0.1:8161/admin
  • 用户名和密码 admin / admin
  • 通过控制台可以获取信息。

2.2 案例

生产者源码

package com.uzong.demo;

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

import javax.jms.*;
import java.util.concurrent.TimeUnit;

/**
 * 消息队列的发送者
 */
public class Sender {

    public static void main(String[] args) throws Exception {

        //1. 创建默认的ConnectionFactory工厂对象,参数:用户、密码 和 URL. 默认端口为:tcp://localhost:61616
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD,
                "tcp://localhost:61616"
        );
        //2. 通过ConnectionFactory创建connection连接,并使用.start() 开启连接。
        Connection connection = connectionFactory.createConnection();
        connection.start();//默认是关闭状态

        //3.通过connection 创建session会话。第一个参数:是否开启事务,第二个参数:签收模式,默认自动
        Session session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);

        //4.通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标和消费信息来源的对象,
        // 在PTP模式中,Destination被称作Queue即队列;在Pub/Sub模式,Destination被称作Topic即主题。
        // 在程序中可以使用多个Queue和Topic。
        Destination destination = session.createQueue("FIRST_QUEUE");

        //5.:我们需要通过Session对象创建消息的发送和接收对象(生产者和消费者)MessageProducer/MessageConsumer。
        MessageProducer messageProducer = session.createProducer(null);

        //6:我们可以使用MessageProducer的setDeliveryMode方法为其设置持久化特性和非持久化特性(DeliveryMode)
        //messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

        // 7:最后我们使用JMS规范的TextMessage形式创建数据(通过Session对象),并用MessageProducer的send方法发送数据。
        // 同理,客户端使用receive方法进行接收数据,最后需要关闭Connection连接。
        for (int i = 0; i < 100; i++) {
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText("我是消息内容......" + i);
            messageProducer.send(destination, textMessage);
            System.out.println("生产者:" + textMessage.getText());
            TimeUnit.SECONDS.sleep(1); //暂停1s
        }

        //生产完成以后,需要手动关闭连接,释放资源。
        if (connection != null) {
            connection.close();
        }
    }
}


消费者源码

package com.uzong.demo;

import org.apache.activemq.ActiveMQConnectionFactory;

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


/**
 * 消息队列的接受者
 */
public class Consumer {
    public static void main(String[] args) throws  Exception{
        // 第一步:建立ConnectionFactory工厂对象,需要填入用户名、密码、以及要连接的地址,均使用默认即可,
        // 默认端口为:tcp://localhost:61616
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnectionFactory.DEFAULT_USER,
                ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                "tcp://localhost:61616");

        // 第二步:通过ConnectionFactory工厂对象我们创建一个Connection链接,
        // 并且调用ConnectionFactory的start方法开启链接,Connection默认是关闭的
        Connection connection = connectionFactory.createConnection();
        connection.start();

        // 第三步:通过Connection对象创建Session会话(上下文环境对象),
        // 用于接收消息,参数配置1为是否启用事务,参数配置2为签收模式,一般我们设置为自动签收
        Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

        // 第四步:通过Session创建Destination对象,指的是一个客户端用来指定生产消息目标
        // 和消费信息来源的对象,在PTP模式中,Destination被称作Queue即队列;
        // 在Pub/Sub模式,Destination被称作Topic即主题。在程序中可以使用多个Queue和Topic。
        Destination destination = session.createQueue("FIRST_QUEUE");

        // 第五步:我们需要通过Session对象创建消息的发送和接收对象(生产者和消费者)
        // MessageProducer/MessageConsumer。
        MessageConsumer messageConsumer = session.createConsumer(destination);

        while (true) {
            TextMessage msg = (TextMessage) messageConsumer.receive();
            if(msg == null){
                break;
            }
            System.out.println("接收到的内容:" + msg.getText());
        }

        if (connection != null) {
            connection.close();
        }

    }
}


三、安全

3.1 管理界面

  • activemq的web管理界面: http://127.0.0.1:8161/admin
  • activemq管控台使用jetty部署,所以需要修改密码则需要到相应的配置文件apache-activemq-5.11.1\conf\jetty-realm.properties 修改。如下:
# 下面的账号密码是管控台的认证
# Defines users that can access the web (console, demo, etc.)
# username: password [,rolename ...]
admin: admin, admin
user: user, user
  • 只有符合认证的用户才能进行发送和获取消息,在activemq.xml里去添加安全验证配置。如下:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
……

<plugins>
	<simpleAuthenticationPlugin>
		<users>
		 	<!-- 在java代码中的连接认证信息 -->
			<authenticationUser username="root" password="123456" groups="users,admins"/>
		</users>
	</simpleAuthenticationPlugin>
</plugins>

</broker>

四、持久化数据源

activeMQ 持久化,可以切换不同的存储技术(默认是kahadb,leveldb,mysql,oracle)

4.1 使用mysql 作为持久化

  • 修改activemq.xml文件
<!--
    <persistenceAdapter>
        <kahaDB directory="${activemq.data}/kahadb"/>
    </persistenceAdapter>
-->
<!-- 修改持久化方式 -->
<persistenceAdapter>
  	<jdbcPersistenceAdapter  dataSource="#mysql-ds"/>
</persistenceAdapter>

<!-- MySql DataSource Sample Setup -->
<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="root"/>
    <property name="password" value="123456"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>
  • 重新启动activemq时,如果报错,发现类没有找到,则缺少相关jar包,下载相关jar到lib目录下。

commons-pool-1.6.jarcommons-dbcp-1.4.jarcommons-pool-1.6.jar

  • 重新运行 activemq.bat

4.2 生成表

配置完成,重新运行代码后,会自动生成三张表:

  • activemq_acks:ActiveMQ的签收信息。
  • activemq_lock: ActiveMQ的锁信息。
  • activemq_msgs: ActiveMQ的消息的信息

五、API详细

5.1 connection

当一个Connection被创建时,它的传输默认是关闭的,必须使用start方法开启。一个Connection可以建立一个或多个的Session。

当一个程序执行完成后,必须关闭之前创建的Connection,否则ActiveMq不能释放资源,关闭一个Connection同样也关闭了Session,MessageProducer和MessageConsumer


5.2 SESSION

Session方法使用:

一旦从ConnectionFactory中获得一个Connection,必须从Connection中创建一个或者多个SessionSession是一个发送或者接收消息的线程,可以使用Session创建MessageProducer,MessageConsumerMessage

Session可以被事务化,也可以不被事务化,通常,可以通过向Connection上的适当创建方法传递一个布尔参数对此进行设置。

Session createSession(boolean transacted ,int acknowledgeMode)

其中transacted为使用事务标识,acknowledgeMode为签收模式。

结束事务有两种方法:提交或者回滚,当一个事务提交,消费被处理、出入事务中有一个步骤失败,事务就回滚,这个事务中的意见执行的动作将被撤销。在发送消息最后也必须要使用session.commit()方法表示提交事务。

签收模式有三种形式:

  • Session.AUTO_ACKNOWLEDGE当客户端从receive或onMessage成功返回时,Session自动签收客户端的这条消息的收条。

  • Session.CLIENT_ACKNOWLEDGE客户端通过调用消息(Message)的acknowledgeMode方法签收消息。这种情况下,签收发生在Session层面:签收一个已消费的消息会自动签收这个Session所有已消费的收条。

//手动签收
msg.acknowledge();
  • Session.DUPS_OK_ACKNOWLEDGE此选项指示Session不必确保对传送消息的签收。它可能引起消息的重复,但是降低了Session的开销,所以只有客户端能容忍重复的消息,才可使用

5.3 MessageProducer

MessageProducer : MessageProducer是一个由Session创建的对象,用来向Destination发送消息。

void send(Destination destination,Message message);

void send(Destination destination,Message message ,int deliveryMode,int priority . long timeToLive);

void send(Message message);

void send(Message message , int deliveryMode,int priority . long timeToLive)

其中deliveryMode为传送模式,priority为消息优先级,timeToLive为消息过期时间。

  • ActiveMQ支持两种消息传送模式:PERSISTENNON_PERSISTENT两种。如果不指定传送模式,那么默认是持久化消息,如果容忍消息丢失,那么使用非持久性消息可以改善性能和减少存储的开销。

  • 消息优先级从0-9十个级别。0-4是普通消息,5-9是加急消息。如果不指定优先级,则默认为4.JMS不要求严格按照这10个优先级发送消息,但必须保证单次加急消息要先于普通消息到达。
    设置优先级需要在activemq.xml中进行配置。 如下:

 <policyEntry queue="queuename"  prioritizedMessages="true" />  
  • 当然ActimeMQ还有一种独有消费模式,可以确保消息顺序

ActiveMQ 的独占消费(Exclusive Consumer)
我们经常希望维持队列中的消息,按一定次序转发给消息者。然而当有多个JMS Session和消息消费者实例的从同一个队列中获取消息的时候,就不能保证消息顺序处理。因为消息被多个不同线程并发处理着。
在ActiveMQ4.x中可以采用Exclusive Consumer或者Exclusive Queues,避免这种情况,Broker会从消息队列中,一次发送消息给一个消息消费者来保证顺序。
配置如下:

queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");
consumer = session.createConsumer(queue);
  1. 当在接收信息的时候有一个或者多个备份接收消息者和一个独占消息者的同时接收时候,无论两者创建先后,在接收的时候,均为独占消息者接收。
  2. 当在接收信息的时候,有多个独占消费者的时候,只有一个独占消费者可以接收到消息。
  3. 当有多个备份消息者和多个独占消费者的时候,当所有的独占消费者均close的时候,只有一个备份消费者接到到消息。

  • 默认情况下,消息永不过期,如果消息在特定周期内失去意义,那么可以设置过期时间,时间单位为毫秒。

5.4 MessageConsumer

MessageConsumer是一个由Session创建的对象,用来从Destination接收消息。

MessageConsumer createConsumer(Destination destination)

MessageConsumer createConsumer(Destination destination,String messageSelector)

MessageConsumer createConsumer(Destination destination,String messageSelector,boolean noLocal)

TopicSubscriber createDurableSubscriber(Topic topic,String name);

TopicSubscriber createDurableSubscriber(Topic topic,String name,String messageSelector,boolean noLocal);

其中messageSelector为消息选择器,noLocal标志默认为false,当设置为true时限制消费者只能接收和自己相同的连接(Conneciton)所发布的消息。此标志只适用于主推,不适用于队列;name标识订阅主题所对应的订阅名称,持久订阅时需要设置此参数。

public final String SELECTOR ="JMS_TYPE = 'MY_TAG1'";该选择器检查了传入消息的JMS_TYPE属性,并确定了这个属性,并确定了这个属性的值是否等于MY_TAG1.如果相等,则消息被消费,如果不相等,那么消息会被忽略。

消息的同步和异步接收:

消息的同步接收是指客户端主动去接收消息,客户端可以采用MessageConsumer的receive方案区接收下一个消息。

  Message receive()

  Message receive(long timeout)

  Message receiveNoWait()

消息的异步接收是指当消息到达时,ActiveMQ主动通知客户端,可以通过注册一个实现MessageListener接口的对象到MessageConsumerMessageListener只有一个必须实现的方案—onMessage,它只接收一个参数,即Message,在为每个发送到Destination的消息实现onMessage时,将调用该方法。


5.5 小案例

生产者

public class Producer {

    private ConnectionFactory connectionFactory;
    private Connection connection;
    private Session session;
    //生成者
    private MessageProducer messageProducer;

    //构造器中初始化四个对象
    public Producer() {
        this.connectionFactory = new ActiveMQConnectionFactory(
                "root",
                "123456",
                "tcp://localhost:61616"
        );
        try {
            this.connection = this.connectionFactory.createConnection();
            this.connection.start();
            this.session = this.connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            this.messageProducer = this.session.createProducer(null);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public Session getSession() {
        return this.session;
    }

    public void send() {
        try {
            Destination destination = this.session.createQueue("FIRST_QUEUE");
            MapMessage msg1 = this.session.createMapMessage();
            msg1.setStringProperty("name", "小米");
            msg1.setIntProperty("age", 25);
            msg1.setIntProperty("sal", 8000);
            MapMessage msg2 = this.session.createMapMessage();
            msg2.setStringProperty("name", "小王");
            //必须要使用setStringProperty 才能是选择过滤起作用
            msg2.setIntProperty("age", 26);
            msg2.setIntProperty("sal", 9000);
            MapMessage msg3 = this.session.createMapMessage();
            msg3.setStringProperty("name", "小李");
            msg3.setIntProperty("age", 26);
            msg3.setIntProperty("sal", 7000);
           // this.messageProducer.send(destination, msg1, DeliveryMode.PERSISTENT, 3, 1000 * 660 * 10L);
           // this.messageProducer.send(destination, msg2, DeliveryMode.PERSISTENT, 6, 1000 * 660 * 10L);
           // this.messageProducer.send(destination, msg3, DeliveryMode.PERSISTENT, 9, 1000 * 660 * 10L);

            this.messageProducer.send(destination, msg1);
            this.messageProducer.send(destination, msg2);
            this.messageProducer.send(destination, msg3);
        } catch (JMSException e) {
            e.printStackTrace();
        }finally {
            if(null != connection) {
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public static void main(String[] args) {
        Producer producer = new Producer();
        producer.send();
    }
}

消费者

public class Consumer {

    // public  final  String  SELECTOR_0 = "age > 25";
    // public  final  String  SELECTOR_1 = "sal > 8500";

    private ConnectionFactory connectionFactory;
    private Connection connection;
    private Session session;
    //消费者
    private MessageConsumer messageConsumer;
    private Destination destination;
    //构造器中初始化四个对象
    public Consumer() {
        this.connectionFactory = new ActiveMQConnectionFactory(
                "root",
                "123456",
                "tcp://localhost:61616"
        );
        try {
            this.connection = this.connectionFactory.createConnection();
            this.connection.start();
            this.session = this.connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            this.destination = this.session.createQueue("FIRST_QUEUE");
            //消费者
            this.messageConsumer = this.session.createConsumer(this.destination);

        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public void receiver() {
        try {
            this.messageConsumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    if(message instanceof  MapMessage){
                        MapMessage msg = (MapMessage) message;
                        try {
                            System.out.println(msg.toString());
                            System.out.println(msg.getStringProperty("name"));
                            System.out.println(msg.getIntProperty("age"));
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.receiver();
    }
}

5.6 模型

  • 点对点模型(Queue)

一个生产者向一个特定的队列发布消息,一个消费者从这个队列中依次读取消息
模型特点:只有一个消费者获得消息

在这里插入图片描述

  • 发布者/订阅者模型(Topic)

0个或多个订阅者可以接受特定主题的消息
模型特点:多个消费者可获得消息

在这里插入图片描述

    public void sendMessage() {
        try {
            //创建发布订阅模式。关键API。createTopic()
            Destination destination = this.session.createTopic("topic");
            TextMessage textMessage = this.session.createTextMessage("我是内容!");
            messageProducer.send(destination,textMessage);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

Topic和Queue的最大区别在于Topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息;而Queue则是以点对点的形式通知多个处于监听状态的客户端中的一个


参考

案例代码地址:https://github.com/uzong/activemq
https://www.cnblogs.com/jaycekon/p/6220200.html
https://blog.csdn.net/fun913510024/article/details/45284379

猜你喜欢

转载自blog.csdn.net/qq_31156277/article/details/80993805
今日推荐