适合初学者的activeMQ讲解

为什么要使用消息中间件?

系统解耦

假设你有个系统A,这个系统A会产出一个核心数据,现在下游有系统B和系统C需要这个数据。
那简单,系统A就是直接调用系统B和系统C的接口发送数据给他们就好了。

整个过程,如下图所示:
适合初学者的activeMQ讲解_第1张图片
但是现在要是来了系统D、系统E、系统F、系统G,等等,十来个其他系统慢慢的都需要这份核心数据呢?如下图所示:
适合初学者的activeMQ讲解_第2张图片
大家可别以为这是开玩笑,一个大规模系统,往往会拆分为几十个甚至上百个子系统,每个子系统又对应N多个服务,这些系统与系统之间有着错综复杂的关系网络。

如果某个系统产出一份核心数据,可能下游无数的其他系统都需要这份数据来实现各种业务逻辑。此时如果你要是采取上面那种模式来设计系统架构,那么绝对你负责系统A的同学要被烦死了。

先是来一个人找他要求发送数据给一个新的系统H,系统A的同学要修改代码然后在那个代码里加入调用新系统H的流程。一会那个系统B是个陈旧老系统要下线了,告诉系统A的同学:别给我发送数据了,接着系统A再次修改代码不再给这个系统B。
然后如果要是某个上游或者下游系统突然宕机了呢?
系统A的调用代码里是不是会抛异常?那系统A的同学会收到报警说异常了,结果他还要去care是下游哪个系统宕机了。所以在实际的系统架构设计中,如果全部采取这种系统耦合的方式,在某些场景下绝对是不合适的,系统耦合度太严重。并且互相耦合起来并不是核心链路的调用,而是一些非核心的场景(比如上述的数据消费)导致了系统耦合,这样会严重的影响上下游系统的开发和维护效率。

因此在上述系统架构中,就可以采用MQ中间件来实现系统解耦。
系统A就把自己的一份核心数据发到MQ里,下游哪个系统感兴趣自己去消费即可,不需要了就取消数据的消费,如下图所示:
适合初学者的activeMQ讲解_第3张图片

异步调用

假设你有一个系统调用链路,是系统A调用系统B,一般耗时20ms;系统B调用系统C,一般耗时200ms;系统C调用系统D,一般耗时2s,如下图所示。
在这里插入图片描述
现在最大的问题就是:
用户一个请求过来巨慢无比,因为走完一个链路,需要耗费:
20ms + 200ms + 2000ms(2s) = 2220ms,

也就是2秒多的时间。但是实际上,链路中的系统A调用系统B,系统B调用系统C,这两个步骤起来也就220ms。就因为引入了系统C调用系统D这个步骤,导致最终链路执行时间是2秒多,直接将链路调用性能降低了10倍,这就是导致链路执行过慢的罪魁祸首。

那此时我们可以思考一下,是不是可以将系统D从链路中抽离出去做成异步调用呢?其实很多的业务场景是可以允许异步调用的。

这样,实现思路就是系统A -> 系统B -> 系统C,直接就耗费220ms后直接成功了。然后系统C就是发送个消息到MQ中间件里,由系统D消费到消息之后慢慢的异步来执行这个耗时2s的业务处理。通过这种方式直接将核心链路的执行性能提升了10倍。整个过程,如下图所示:
适合初学者的activeMQ讲解_第4张图片

流量削峰

假设你有一个系统,平时正常的时候每秒可能就几百个请求,系统部署在8核16G的机器的上,正常处理都是OK的,每秒几百请求是可以轻松抗住的,但是如下图所示,在高峰期一下子来了每秒钟几千请求,弹指一挥间出现了流量高峰,此时你的选择是要搞10台机器,抗住这个瞬时高峰吗?
适合初学者的activeMQ讲解_第5张图片
假设瞬时高峰每天就那么半个小时,其它时间基本为每秒就几百请求,如果你线上部署了很多台机器,那么每台机器就处理每秒几十个请求就可以了,这不是有点浪费机器资源吗?大部分时候,每秒几百请求,一台机器就足够了,但是为了抗那每天瞬时的高峰,硬是部署了10台机器,每天就那半个小时有用,别的时候都是浪费资源的。

是什么

定义

适合初学者的activeMQ讲解_第6张图片

特点

1、采用异步处理模式
适合初学者的activeMQ讲解_第7张图片
在这里插入图片描述
2、应用系统间解耦
发送者和接受者无需了解对方,只需发送或者接收消息即可。并且发送者和接受者无需同时在线。

activeMQ安装

下载

去官网http://activemq.apache.org/,下载最新安装包,我使用的是5.15.10版本。
将安装包apache-activemq-5.15.10-bin.tar下载到根目录的/opt下。

解压

使用

tar -zxvf apache-activemq-5.15.10-bin.tar

对其进行解压,并把挤压后的文件放到根目录的/myactivemq/文件夹下。

启动

cd /myactivemq/apache-activemq-5.15.10/bin

进入activemq目录下的bin目录,使用

./activemq start

启动activeMQ。
也可以

./activemq status

查看activeMQ启动状态。

./activemq stop

关闭。

验证

在启动ActiveMQ之后,ActiveMQ会开放两个端口,一个是后台端口61616,另一个是前台端口8161.我们可以使用ip+端口号进行访问,如http://192.168.0.119:8161进行访问。能看到如下画面,即启动成功。
适合初学者的activeMQ讲解_第8张图片

Java编码实现ActiveMQ通信

建立maven工程

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rocket</groupId>
    <artifactId>test_activemq</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.targer>1.8</maven.compiler.targer>
    </properties>
    <dependencies>
        <!--activemq需要的jav包-->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.16</version>
        </dependency>
        <!--下面是junit/log4等通用配置-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

ActiveMQ中的目的地有两种:queue和topic,其中队列是点对点的传输,只能有一个接收方,而topic可以有多个接收方,只要再发布消息之前订阅了该topic就可以接收到,并且实现了“负载均衡”,即多个接受者间隔接受。
适合初学者的activeMQ讲解_第9张图片

点对点传输

在点对点传输下,目的地为队列queue。

生产者编码

package com.atguigu.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

/**
 * @ClassName JmsProduce
 **/
public class JmsProduce {
    public static final String URL = "tcp://192.168.0.119:61616";
    public static final String queue_name = "queue01";

    public static void main(String[] args) throws JMSException {
        //创建连接工厂
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(URL);
        //利用工厂获取connection,并开始连接
        Connection connection = factory.createConnection();
        connection.start();
		//创建会话session,两个参数:事务和签收
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        //创建目的地Destination
        Queue queue = session.createQueue(queue_name);
        //创建生产者
        MessageProducer producer = session.createProducer(queue);
		//生产者发送消息
        for (int i = 1; i < 3; i++) {
            TextMessage textMessage = session.createTextMessage("mag..." + i);
            producer.send(textMessage);
        }
        //关闭资源
        producer.close();
        session.close();
        connection.close();
        System.out.println("生产者生产数据成功!");
    }
}

运行生产者代码之后ActiveMQ控制台如下所示:
适合初学者的activeMQ讲解_第10张图片

消费者编码

package com.atguigu;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
 * ActiveMQ
 * JMS(Java Message Service)的生产者编码
 * @author xxy
 * @date 2019/12/16
 */
public class JmsConsumer {
    private static final String ACTIVE_MQ = "tcp://192.168.0.119:61616";
    private static final String QUEUE_NAME = "queue01"
    
    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("消费者01");
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(ACTIVE_MQ);
        Connection connection = factory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Queue queue = session.createQueue(QUEUE_NAME);
        MessageConsumer consumer = session.createConsumer(queue);
       /*
       同步阻塞方式receive()
        while (true) {
            TextMessage message = (TextMessage) consumer.receive();
            if (null != message) {
                System.out.println("消费者受到消息,内容是:" + message.getText());
            } else {
                break;
            }
        }
        consumer.close();
        session.close();
        connection.close();*/
        
        //监听器方式
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (null != message && message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("msg-----" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

消费者消息的接受存在两种方式,一种是同步阻塞方式,另一种是监听器方式。
同步阻塞方式中有receive()和receive(long time)两个方法,没有消息阻塞在这里或者没有消息超过一定时间之后退出。监听器方式为设置一个监听器,监听是否有消息传送过来。

基于发布订阅

生产者

package com.atguigu;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

/**
 * ActiveMQ
 * JMS(Java Message Service)的生产者编码
 * @author xxy
 * @date 2019/12/16
 */
public class JmsProduceTopic {
    private static final String ACTIVE_URL = "tcp://192.168.0.119:61616";
    private static final String TOPIC_NAME = "topic-01";

    public static void main(String[] args) throws JMSException {
        //创建连接工厂,按照指定的url地址,采用默认用户名和密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVE_URL);
        //通过连接工厂创建连接,并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        //创建回话session,两个参数:事务和签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //常见目的地(队列或queue者主题topic)
        Topic topic = session.createTopic(TOPIC_NAME);
        //创建生产者
        MessageProducer producer = session.createProducer(topic);
        //使用MessageProducer生产3条消息发送到MQ的队列里
        for (int i = 0; i < 3; i++) {
            //创建消息
            TextMessage textMessage = session.createTextMessage("msg------" + i);
            //通过生产者MessageProducer发送给MQ
            producer.send(textMessage);
        }
        //关闭资源
        producer.close();
        session.close();
        connection.close();
    }
}

消费者

package com.atguigu;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

/**
 * ActiveMQ
 * JMS(Java Message Service)的消费者编码
 * @author xxy
 * @date 2019/12/16
 */
public class JmsConsumerTopic {
    private static final String ACTIVEMQ_URL = "tcp://192.168.0.119:61616";
    private static final String TOPIC_NAME = "topic-01";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("消费者TOPIC-3");
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        Connection connection = factory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(TOPIC_NAME);
        MessageConsumer consumer = session.createConsumer(topic);
        //监听器方式
        consumer.setMessageListener((message) -> {
            if (null != message && message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                try {
                    System.out.println("msg-----" + textMessage.getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

queue和topic两种模式的对比

适合初学者的activeMQ讲解_第11张图片

你可能感兴趣的:(消息中间件,ActiveMQ,中间件)