ActiveMq-基础知识

一、简单介绍

1. 啥是中间件?

通俗的说,就是不是给用户直接使用,也不处理系统业务的一个中间服务。可以看作一个团队里的辅助,有了它可以增益全队,少了也没啥大事。

2. 为啥要用消息中间件?

场景一:
首先来说一个经典场景,商品秒杀。如0元秒杀10件商品,就可能有十万大佬在抢。同一时间这么大的访问压力,随随便便就把服务干宕机了。这时,就可以用中间件来缓存这些用户的请求,然后慢慢的处理消费掉。
场景二:
平常我们在转账或者取钱后,都会接收到短信通知。这里发送短信就是在处理完业务后,生成一个发送的消息丢到消息中间件中,等待消费异步处理
场景三:
经常看直播的小伙伴,都知道订阅自己喜欢的主播。一旦主播上线,就会通知到你,该去刷飞机火箭了~~~

以上三个是比较经典的使用场景。简单的概括一下,使用消息中间件可以实现一下三点:
1.流量削峰减轻服务压力。
2.实现业务操作的解耦合,实现异步化。
3.发布订阅

3. 啥叫JMS?

JMS即Java消息服务(Java Message Service的简称),是Java EE 的标准/规范之一。这种规范(标准)指出:消息的发送应该是异步的、非阻塞的。也就是说消息的发送者发送完消息后就直接返回了,不需要等待接收者返回后才能返回,发送者和接收者可以说是互不影响。所以这种规范(标准)能够减轻或消除系统瓶颈,实现系统之间去除耦合,提高系统的整体可伸缩性和灵活性。JMS只是Java EE中定义的一组标准API,它自身并不是一个消息服务系统,它是消息传送服务的一个抽象,也就是说它定义了消息传送的接口而并没有具体实现。

  1. 啥时ActiveMq?
    ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。
    它是Apache下的一个项目,采用Java语言开发;是一款非常流行的开源消息服务器
    简单来说,就是JMS一种实现。ActiveMQ是一个中间消息服务,你可以直接使用这个服务完成消息处理。而JMS其实就像一张设计图纸,
    上面勾画定义设计的模型,提供了设计方式,但未实现。

看一张图就明白了他们的关系了。

activemq.png

二、Activemq使用

(1) 环境搭建

在讲原理前,我们先实践操作一下,来了解一下基础的搭建和使用。
第一步:
不用多说。activemq使用java编写的,所以先来安装一个jdk在部署的服务上。这个自行百度解决吧~~
第二步:
下载http://activemq.apache.org/activemq-5140-release.html,这里我这里选择linux版的。
第三步:
上传apache-activemq-5.14.0-bin.tar.gz文件到 /usr/local/ 目录下
第四步:
解压 tar -zxvf apache-activemq-5.14.0-bin.tar.gz ,然后 mv apache-activemq-5.14.0-bin activemq
第五步:
执行: cd activemq/bin ,进入bin目录里。 启动:./activemq start ;关闭: . / activemq stop
第六步:
开放端口 8161:web控制台。61616:消息服务broker连接端口。
这里就不多介绍,我自己是买的云服务器,所以登陆阿里开发平台,设置开放端口就好。如果是本地linux虚拟机,就自行百度解决就好了。
第七步:
打开浏览器输入http://IP:8161/admin,输入用户名和密码 admin/admin

222.png

(2) 基础介绍

消息服务broker URL地址 : tcp : // localhost : 61616

交互方式:

1.点对点( Point-to-Point):专门用于使用队列Queue传送消息;基于队列Queue的点对点消息只能被一个消费者消费,如多个消费者都注册到同一个消息队列上,当生产者发送一条消息后,而只有其中一个消费者会接收到该消息。(一对一的)
2.发布/订阅(Publish/Subscribe):专门用于使用主题Topic传送消息。基于主题的发布与订阅消息能被多个消费者消费,生产者发送的消息,所有订阅了该topic的消费者都能接收到。(一对多或多对多)
接口API定义:
ConnectionFactory:用于创建连接到消息中间件的连接工厂。
Connection:代表了应用程序和服务之间的连接通路。
Destination:指消息发布的地点,包括队列模式和主体模式。
Session:表示一个单线程的上下文,用于发送和接受消息。
MessageConsumer:由会话创建,用于接受发送到目的的消息。
MessageProducer:由会话创建,用于发送消息。
Message:是在消费者和生产者之间传递的对象,消息头,一组消息属性,和一个消息体。
关系为:
一旦有了ConnectionFactory,就可以创建Connection,一旦有了Connection,就可以创建Session,而一旦有了Session,就可以创建 Message 、MessageProducer 和 MessageConsumer 。

api.png

(3) 简单的使用demo

导入maven依赖:

<!--添加activemq的依赖-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemqall</artifactId>
<version>5.9.0</version>
</dependency>

代码演示(点对点):

生产者:
package com.onereader.webblog.common.configure.template.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.configure.template
 * @ClassName: Sender
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/5 23:42
 * @Version: 1.0
 */
public class Sender {
    /**消息服务器的连接地址**/
    public static final String BROKER_URL = "tcp://xxx.xxx.xxx.xxx:61616";

    public static void main(String[] args) {
        Sender sender = new Sender();
        sender.sendMessage("Hello ActiveMQ.");
    }

    /**
     * 发送消息
     *1. session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
     * 其中:Boolean.FALSE表示本次会话不开启事务管理,假如需要开启事务管理,将其改为Boolean.TRUE即可
     * 同时需要在发送消息后添加session.commit(),否则,消息是不会被提交的.
     * Session.AUTO_ACKNOWLEDGE表示消息确认机制
     * AUTO_ACKNOWLEDGE:自动确认
     * CLIENT_ACKNOWLEDGE:客户端确认
     * SESSION_TRANSACTED:事务确认,如果使用事务推荐使用该确认机制
     * AUTO_ACKNOWLEDGE:懒散式确认,消息偶尔不会被确认,也就是消息可能会被重复发送.但发生的概率很小
     * 2. connection.start();
     * 在消息接收端,接受消息前需要加入这段代码,开启连接,否则一样无法获取消息.
     * 3. Destination destination = session.createQueue("myQueue");
     * 创建目的地时,如果做测试收不到信息,可以将目的地名称修改一下,我用的是IDEA,不清楚为何,
     * 有时候收不到信息,修改一下就好了,猜测可能是缓存的原因吧
     * @param msg
     */
    public void sendMessage (String msg) {

        Connection connection = null;
        Session  session = null;
        MessageProducer messageProducer = null;

        try {
            //1.创建一个连接工厂
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            //2.创建一个连接
            connection = connectionFactory.createConnection();
            //3.创建一个Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            //4.创建消息,此处创建了一个文本消息
            Message message = session.createTextMessage(msg);
            //5.创建一个目的地
            Destination destination = session.createQueue("myQueue");
            //6.创建一个消息的生产者(发送者)
            messageProducer = session.createProducer(destination);
            //7.发送消息
            messageProducer.send(message);
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭连接释放资源
                if (null != messageProducer) {
                    messageProducer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者:
package com.onereader.webblog.common.configure.template.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.configure.template
 * @ClassName: Receiver
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/5 23:47
 * @Version: 1.0
 */
public class Receiver {

    /**消息服务器的连接地址**/
    public static final String BROKER_URL = "tcp://xxx.xxx.xxx.xxx:61616";

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

    /**
     * 接收消息
     *
     */
    public void receiveMessage () {

        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;

        try {
            //1.创建一个连接工厂
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
            //2.创建一个连接
            connection = connectionFactory.createConnection();
            //接收消息之前,需要把连接启动一下
            connection.start();
            //3.创建一个Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
            //4.创建一个目的地
            Destination destination = session.createQueue("myQueue");
            //5.创建一个消息的消费者(接收者)
            messageConsumer = session.createConsumer(destination);
            //6.接收消息,创建消费的监听
            messageConsumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    //判断消息的类型
                    if (message instanceof TextMessage) {
                        String text = null;
                        try {
                            text = ((TextMessage) message).getText();
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                        System.out.println("接收到的消息内容是:" + text);
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭连接释放资源
                if (null != messageConsumer) {
                    messageConsumer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }

}
			

代码演示(主题订阅):

这里不想粘代码了,只需要修改以上代码的第四步为:
Destination destination = session.createTopic(“myTopic”);

(4) 使用配置介绍

掌握了上面的基础是使用,是不是对activemq发送接收过程有些了解了呢?
下面来说一下几个注意事项:

1.ActiveMq的消息类型

1、TextMessage 文本消息:携带一个java.lang.String作为有效数据(负载)的消息,可用于字符串类型的信息交换;
2、ObjectMessage 对象消息:携带一个可以序列化的Java对象作为有效负载的消息,可用于Java对象类型的信息交换;
3、MapMessage 映射消息:携带一组键值对的数据作为有效负载的消息,有效数据值必须是Java原始数据类型(或者它们的包装类)及String。即:byte , short , int , long , float , double , char , boolean , String
4、BytesMessage 字节消息 :携带一组原始数据类型的字节流作为有效负载的消息;
5、StreamMessage 流消息:携带一个原始数据类型流作为有效负载的消息,它保持了写入流时的数据类型,写入什么类型,则读取也需要是相同的类型;
以上五种方式,就不多介绍了。日常使用中,可以根据自己的习惯或者业务场景选择使用。

2.ActiveMQ事务消息

session=connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
在创建session时可以选择开启事务控制。所谓事务控制,即将消息生成发送,或接受,消费操作放入一个事务中。但不能同时控制发送与消费这一整个过程。因为事务都是基于一个session下的操作。

简单来说,如是生产者发送消息到mq中间件这个过程为例。
创建一个带事务的session后,我们在使用生产者去生产消息,此时消息中间件服务上是看不到消息的。而当我们使用session.commit()后,才能看到了消息到中间件。那么在commit之前,如果出现任何问题都可以使用session.rollback();来回滚操作。

3.ActiveMQ签收模式

(1)、客户接收消息;(2)、客户处理消息;(3)、消息被确认;
确认机制前面提过一下,共有四种:
(1)、Session.AUTO_ACKNOWLEDGE;客户(消费者)成功从receive方法返回时,或者从MessageListener.onMessage方法成功返回时,会话自动确认消息,然后自动删除消息.
(2)、Session.CLIENT_ACKNOWLEDGE;客户通过显式调用消息的acknowledge方法确认消息,。 即在接收端调用message.acknowledge();方法,否则,消息是不会被删除的.
(3)、Session. DUPS_OK_ACKNOWLEDGE ;不是必须确认,是一种“懒散的”消息确认,消息可能会重复发送,在第二次重新传送消息时,消息头的JMSRedelivered会被置为true标识当前消息已经传送过一次,客户端需要进行消息的重复处理控制。
(4)、 Session.SESSION_TRANSACTED;事务提交并确认。

4.ActiveMQ消息持久化

//不持久化 messageProducer.setDeliveryMode(DeliveryMode. NON_PERSISTENT);
//持久化的,默认都是持久化的 messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
设置完后,如果为持久化,那么消息在没有被消费前都会被写入本地磁盘kahadb文件中保存起来,即使服务器宕机,也不会影响消息.如果是非持久化的,那么,服务一旦宕机之类的情况发生,消息即会被删除

5.ActiveMQ消息过滤

ActiveMQ提供了一种机制,可根据消息选择器中的标准来执行消息过滤,只接收符合过滤标准的消息;
生产者可在消息中放入特有的标志,而消费者使用基于这些特定的标志来接收消息;

使用:

Producer加入:

//将消息设置一个特有的标识 
message.setIntProperty("id", 11);

Comsumer加入:

//selector即为消息选择器,通过选择需要的标识,过滤消息接受id为10-15之 间的消息 
String selector = "id >=10 and id<=15"; 
messageConsumer = session.createConsumer(destination, selector)

(5) springboot集成ActiveMq

application.yml

#activeMq
activemq:
  #broker地址
  url: tcp://xxx.xxx.xxx.xxx:61616
  #用户名
  usrName: admin
  #密码
  password: admin
  #队列名
  queueName: myQueue
  #主题名
  topicName: myTopic

ActiveMqConfigure.java

package com.onereader.webblog.common.configure.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.configure
 * @ClassName: ActiveMqConfigure
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/10 0:26
 * @Version: 1.0
 */
@Getter
@Setter
@Component
public class ActiveMqConfigure {
    /**
     * activemq地址
     */
    @Value("${activemq.url}")
    private String brokerUrl;
    /**
     * 队列名
     */
    @Value("${activemq.queueName}")
    private String queueName;
    /**
     * 主题名
     */
    @Value("${activemq.topicName}")
    private String topicName;
    /**
     * 用户名
     */
    @Value("${activemq.usrName}")
    private String usrName;
    /**
     * 密码
     */
    @Value("${activemq.password}")
    private  String password;

}

ExtMqBeanConfigure.java

package com.onereader.webblog.common.configure;

import com.onereader.webblog.common.configure.config.ActiveMqConfigure;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import javax.annotation.Resource;
import javax.jms.Queue;
import javax.jms.Topic;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.factory
 * @ClassName: ExtActiveMqConfigure
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/10 16:04
 * @Version: 1.0
 */
@Configuration
public class ExtMqBeanConfigure {

    /**
     * mq配置
     */
    @Resource
    private ActiveMqConfigure activeMqConfigure;

    /**
     * 创建队列
     * @return
     */
    @Bean
    public Queue queue(){
        return new ActiveMQQueue(activeMqConfigure.getQueueName());
    }

    /**
     * 创建主题
     * @return
     */
    @Bean
    public Topic topic(){
        return new ActiveMQTopic(activeMqConfigure.getTopicName());
    }


    /**
     * mq连接工厂
     * @return
     */
    @Bean
    public ActiveMQConnectionFactory connectionFactory() {
        return new ActiveMQConnectionFactory(activeMqConfigure.getUsrName(), activeMqConfigure.getPassword(), activeMqConfigure.getBrokerUrl());
    }

    /**
     * 队列消息监听工厂
     * @param connectionFactory
     * @return
     */
    @Bean
    public JmsListenerContainerFactory<?> jmsListenerContainerQueue(ActiveMQConnectionFactory connectionFactory){
        DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
        bean.setConnectionFactory(connectionFactory);
        return bean;
    }

    /**
     * 主题消息监听工厂
     * @param connectionFactory
     * @return
     */
    @Bean
    public JmsListenerContainerFactory<?> jmsListenerContainerTopic(ActiveMQConnectionFactory connectionFactory){
        DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
        //设置为发布订阅方式, 默认情况下使用的生产消费者方式
        bean.setPubSubDomain(true);
        bean.setConnectionFactory(connectionFactory);
        return bean;
    }


}

QueueListener.java

package com.onereader.webblog.common.configure.listener;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.configure.listener
 * @ClassName: QueueListener
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/10 16:32
 * @Version: 1.0
 */
@Component
public class QueueListener {

    @JmsListener(destination = "myQueue", containerFactory = "jmsListenerContainerQueue")
    public void receive(String text){
        System.out.println("QueueListener:收到一条信息: " + text);
    }
}

TopicListener.java

package com.onereader.webblog.common.configure.listener;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.common.configure.listener
 * @ClassName: TopicListener
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/10 16:35
 * @Version: 1.0
 */
@Component
public class TopicListener {

    @JmsListener(destination = "myTopic", containerFactory = "jmsListenerContainerTopic")
    public void receive(String text){
        System.out.println("TopicListener: 收到一条信息: " + text);
    }
}

TestController.java

package com.onereader.webblog.controller.sys;

import com.onereader.webblog.common.bean.ResponseBase;
import com.onereader.webblog.common.configure.template.JMSSendMsgUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @ProjectName: onereader
 * @Package: com.onereader.webblog.controller.sys
 * @ClassName: TestController
 * @Author: onereader
 * @Description: ${description}
 * @Date: 2019/8/10 18:05
 * @Version: 1.0
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private JMSSendMsgUtils jmsSendMsgUtils;


    @RequestMapping(value = "/sendMsg",method = RequestMethod.GET)
    public ResponseBase sendMsg(){
        for (int i=0 ;i<10 ;i++){
            jmsSendMsgUtils.sendQueueMessage("一个myQueue中消息("+i+")");
            jmsSendMsgUtils.sendTopicMessage("一个myTopic中消息("+i+")");
        }
        return new ResponseBase();
    }

}

启动项目,使用postman发送一个请:localhost:8000/test/sendMsg

查看管理台结果显示:

QueueListener:收到一条信息: 一个myQueue中消息(0)
TopicListener: 收到一条信息: 一个myTopic中消息(0)
QueueListener:收到一条信息: 一个myQueue中消息(1)
TopicListener: 收到一条信息: 一个myTopic中消息(1)
QueueListener:收到一条信息: 一个myQueue中消息(2)
TopicListener: 收到一条信息: 一个myTopic中消息(2)
QueueListener:收到一条信息: 一个myQueue中消息(3)
TopicListener: 收到一条信息: 一个myTopic中消息(3)
QueueListener:收到一条信息: 一个myQueue中消息(4)
TopicListener: 收到一条信息: 一个myTopic中消息(4)
QueueListener:收到一条信息: 一个myQueue中消息(5)
TopicListener: 收到一条信息: 一个myTopic中消息(5)
QueueListener:收到一条信息: 一个myQueue中消息(6)
TopicListener: 收到一条信息: 一个myTopic中消息(6)
QueueListener:收到一条信息: 一个myQueue中消息(7)
TopicListener: 收到一条信息: 一个myTopic中消息(7)
QueueListener:收到一条信息: 一个myQueue中消息(8)
TopicListener: 收到一条信息: 一个myTopic中消息(8)
QueueListener:收到一条信息: 一个myQueue中消息(9)
TopicListener: 收到一条信息: 一个myTopic中消息(9)

到此就暂时结束activeMq基础总结,至于mq使用过程中的所遇到到的问题的解决方案的思路,都会在以后总结的~ 另外,activemq集群等也不再本次总结之内,下次见咯~

猜你喜欢

转载自www.cnblogs.com/one-reader/p/11360914.html
今日推荐