MQ的概念及用ActiveMQ实现一个生产者多个消费者共享消息队列

MQ的优点及使用场景:

消息总线(Message Queue),后文称MQ,是一种跨进程的通信机制,用于上下游传递消息。

这里写图片描述
在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。

使用了MQ之后,消息发送上游只需要依赖MQ,逻辑上和物理上都不用依赖其他服务。

什么时候不使用消息总线?
既然MQ是互联网分层架构中的解耦利器,那所有通讯都使用MQ岂不是很好?这是一个严重的误区,调用与被调用的关系,是无法被MQ取代的。
MQ的不足是:

  1. 系统更复杂,多了一个MQ组件
  2. 消息传递路径更长,延时会增加
  3. 消息可靠性和重复性互为矛盾,消息不丢不重难以同时保证
  4. 上游无法知道下游的执行结果,这一点是很致命的

举个例子:用户登录场景,登录页面调用passport服务,passport服务的执行结果直接影响登录结果,此处的“登录页面”与“passport服务”就必须使用调用关系,而不能使用MQ通信。
无论如何,记住这个结论:调用方实时依赖执行结果的业务场景,请使用调用,而不是MQ。

MQ它是一个高效的可嵌入库,它解决了大部分应用程序需要解决的问题,变得在网络上有良好的可伸缩性,而没有多少成本。它能在后台线程异步处理I/O。这些线程使用无锁数据结构与应用程序线程进行通信,所以并发.MQ 应用程序不再需要锁、信号量,或其他等待状态。
组件可以动态地来去自如,而MQ 会自动重新连接。这意味着你可以以任何顺序启动组件。你可以创建“面向服务的架构”(SOA),其中的服务可以在任何时间加入和离开网络。

JMS

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持(百度百科给出的概述)。我们可以简单的理解:两个应用程序之间需要进行通信,我们使用一个JMS服务,进行中间的转发,通过JMS 的使用,我们可以解除两个程序之间的耦合。

JMS有以下两个优点:

  1. Asynchronous(异步)JMS 原本就是一个异步的消息服务,客户端获取消息的时候,不需要主动发送请求,消息会自动发送给可用的客户端
  2. Reliable(可靠)JMS保证消息只会递送一次。大家都遇到过重复创建消息问题,而JMS能帮你避免该问题。

JMS通信模式:

1.Point-to-Point Messaging Domain(点对点通信模型) 消息不可重复消费
模式图:

点对点通信模式图

点对点模式下,应用程序由消息队列(Queue),发送方(Client1,Sends),接收方(Client2,Consumes)组成。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

每一条消息只能由一个消费者消费,消费不可重复消费;
接收方(消费者)可随时向消费队列请求消费信息,无论发送方(生产者)是否在发送消息;

2.Publish/Subscribe Messaging Domain(发布/订阅通信模型) 消息可以重复消费
模式图:
这里写图片描述

ActiveMQ实现一个生产者多个消费者共享消息队列

本文实现消息队列环境如下:
jdk版本:1.7.0_79
ActiveMQ:apache-activemq-5.14.0附下载地址:该版本下载地址
开发工具:Intellij IDEA(企业版)
1.ActiveMQ的启动:
下载完毕并解压后,打开控制台,到解压好的ActiveMQ的bin目录下
输入activemq start
打开浏览器输入localhost:8161
看到如下场景(默认登陆名和登陆密码都为:admin)
这里写图片描述
2ActiveMQ下JMS编程模型的实现:
管理对象(Administered objects)-连接工厂(Connection Factories)和目的地(Destination)
连接对象(Connections)
会话(Sessions)
消息生产者(Message Producers)
消息消费者(Message Consumers)
消息监听者(Message Listeners)
(1)连接工厂(Connection Factories)

ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);

其中ActiveMQConnection.DEFAULT_BROKER_URL为默认的activemq设置好的参数,包括用户名,密码以及网络地址(localhost:8161)
也可以如下设置:

ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616");

参数分别为设置好的用户名,密码,以及映射到的tcp或者直接设置url

(2)从工厂中得到连接对象并启动对象

connection = connectionFactory.createConnection();
connection.start();

(3)设置会话(session)

 session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);

session从connection对象中得到,第一个参数为false表示为非事务类型;第二个参数为消息的确认类型,Session.AUTO_ACKNOWLEDGE表示消息自动确认。
(4)设置消息目的地(Destination)

Destination destination = session.createQueue("MyQueue");

消息目的地从会话对象session创建,这里实现消费的不可重复消费,所以创建Queue对象,表示消息将发布到MyQueue的队列中。若要实现发布/订阅模式,使用session.createTopic(“name”);
(5)生产者,消费者
生产者:

producer = session.createProducer(destination);

消费者:

consumer = session.createConsumer(destination);

多个消费者的实现
创建一个consumer类,实现Runnable接口,从而在主函数中启动多个消费者。
消费者的代码实现如下:

package com.message;

import java .util.Date;
import java.text.SimpleDateFormat;

import javax.jms.*;

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

public class Consumer implements Runnable{
    //消费者名称
    private String name;
    private MessageConsumer consumer;
    private Session session;
    private Connection connection = null;
    //设置发布地址为默认的URL地址
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL;
    public Consumer(String name) throws JMSException{
        this.name = name;
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

        connection = connectionFactory.createConnection();

        connection.start();
        //设置第一个参数为true,事务类型数据(应该可以进一步确保不重复消费)
        session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        //设置每次处理的最大消息数为1
        Destination destination = session.createQueue("MyQueue?consumer.prefetchSize=1");

        consumer = session.createConsumer(destination);

    }

    public boolean receive()throws JMSException {
        //设置消息响应等待5s
        TextMessage msg = (TextMessage) consumer.receive(5000);
        if (msg == null){
            System.out.println("消费者"+this.name+"消息队列为空!!!");
            return false;

        }
        else {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            String nowTime = df.format(new Date());
            System.out.println("消费者"+this.name+"在时刻:"+nowTime+"消费数据:\n"+msg.getText());
            return true;
        }


    }

    public void run(){

        try{
            while(true){
                this.receive();
                //线程随机等待0-10s的时间
                long waitTime = (long)(Math.random()*1000);
                try {
                    Thread.sleep(waitTime);

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

    }

    public void close() throws JMSException {
        consumer.close();
        session.close();
        connection.close();
    }




}

附上生产者代码实现如下:

package com.message;

import java .util.Date;
import java.text.SimpleDateFormat;

import javax.jms.*;

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



public class Producer {
    //一次性生产的消息数量
    private static final int PRODUCER_NUMBER = 5;
    //设置发布地址为默认的URL地址
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL;

    private Connection connection = null;
    private MessageProducer producer;
    private Session session;

    public Producer() throws JMSException{
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);


            //创建从工厂连接中得到的对象
            connection = connectionFactory.createConnection();
            connection.start();
            //fasle:参数表示为非事务类型;AUTO_ACNOWLEDGE:消息自动确认
            session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
            Destination destination = session.createQueue("MyQueue");

            producer = session.createProducer(destination);
            //设置消息非持久化(学习用法)
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);


    }
    public void sendMessage() throws JMSException{
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String nowTime = df.format(new Date());
        for (int i = 1;i<= PRODUCER_NUMBER;i++){
                TextMessage message = session.createTextMessage(" 生产者的消息数据------时刻:"+nowTime+"编号:"+i);
                System.out.println("向队列中发送消息:"+i);
                producer.send(message);
        }
        System.out.println("生产者生产5个数据完毕--------");

    }
    public void close() throws JMSException {
        producer.close();
        session.close();
        connection.close();
    }





}

附上主函数调用生产者消费者如下:

package com.message;

import javax.jms.JMSException;
/**
 * 创建一个生产者,三个消费者
 * 利用activeMQ对消息队列进行学习
 *
 */
public class App {
    public static void main(String[] args) {

        try {
                Producer producer = new Producer();
                producer.sendMessage();
                //创建3个消费者线程
                Consumer consumerA = new Consumer("A");
                Consumer consumerB = new Consumer("B");
                Consumer consumerC = new Consumer("C");
                new Thread(consumerA).start();
                new Thread(consumerB).start();
                new Thread(consumerC).start();


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


    }
}

附控制台输出和网页端:

这里写图片描述
网页端显示:
这里写图片描述

【1】JMS消息队列–JMS概述

猜你喜欢

转载自blog.csdn.net/qq_37856804/article/details/81501591
今日推荐