ActiveMQ经典的使用模式(利用多线程处理消费端)

今天看视频,里面讲了一个经典的例子,是工作中很常用的,特此将这种模式记录下来.这个例子使用了ActiveMQ的选择器,也使用了

之前学的自定义线程池.队列的使用,而且很好的利用多线程并发的处理了任务,提高了吞吐量.

首先看生产端:

package com.jvm.activemq.bhz.mq;

import com.jvm.util.PropertiesUtil;
import org.apache.activemq.ActiveMQConnectionFactory;

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

/**
 *非常经典的例子,将任务分发给消费者,消费者使用多线程来处理,很好的提高了系统的吞吐量
 *
 * */
public class Producter {
    //1.连接工厂
    private ConnectionFactory connectionFactory;
    //2.连接对象
    private Connection connection;
    //3.Session对象
    private Session session;
    //4.生产者对象
    private MessageProducer messageProducer;

    public Producter() {
        try {
            //初始化连接
            connectionFactory = new ActiveMQConnectionFactory(
                    ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                    PropertiesUtil.getValue("activemq.properties", "url")
            );
            this.connection = connectionFactory.createConnection();
            connection.start();
            this.session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            //创建生产者
            messageProducer = this.session.createProducer(null);

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

    public void send() {
        try {
            //创建队列
            Destination destination = session.createQueue("first");
            //循环发送100个消息,
            for (int i = 0; i < 100; i++) {
                MapMessage message = session.createMapMessage();
                message.setInt("id", i);
                message.setString("name", "张" + i);
                message.setString("age", "" + i);
                //根据id不同,把消息分为2中,一种奇数,一种偶数,也就是说消费端会使用选择器,有选择的处理消息
                String receiver = i % 2 == 0 ? "A" : "B";
                //注意使用setStringProperty()方法,选择器过滤是根据这个方法过滤的
                message.setStringProperty("receiver", receiver);
                //发送消息(非持久化,优先级为2,即普通消息,存活时间为1分钟
                this.messageProducer.send(destination,message,DeliveryMode.NON_PERSISTENT,2,1000*60L);
                System.out.println("Message send "+ i);
            }
            this.closeConnection(connection);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
    
    //关闭连接
    public void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }

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

然后创建2个消费端:

package com.jvm.activemq.bhz.mq;

import com.jvm.util.PropertiesUtil;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ConsumerA {
    public static final String SELECTOR = "receiver='A'"; //特别注意此处的写法,必须加单引号,类似于sql语句
    //1.连接工厂
    private ConnectionFactory connectionFactory;
    //2.连接对象
    private Connection connection;
    //3.Session对象
    private Session session;
    //4.生产者对象
    private MessageConsumer messageConsumer;
    //目的地
    Destination destination;

    public ConsumerA() {
        try {
            connectionFactory = new ActiveMQConnectionFactory(
                    ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                    PropertiesUtil.getValue("activemq.properties", "url")
            );
            this.connection = connectionFactory.createConnection();
            connection.start();
            this.session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            this.destination = session.createQueue("first");
            messageConsumer = this.session.createConsumer(destination, SELECTOR);
            System.out.println("ConsumerA ...start");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public void receiver() {
        try {
            //利用消息监听器来实现接收消息,而不是while(true)
            this.messageConsumer.setMessageListener(new Listener());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
    //此处很经典,很好的利用了多线程和队列的技术
    class Listener implements MessageListener{
        //创建一个有界阻塞队列
        BlockingQueue queue = new ArrayBlockingQueue<>(1000);
        //创建一个自定义的线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(), //corePoolSize
                20,//maximumPoolSize
                120L, //keepAliveTime
                TimeUnit.SECONDS, //单位
                queue             //使用的队列
        );
        @Override
        public void onMessage(Message message) {
            
            if (message instanceof MapMessage) {
                //利用线程池开启多个线程去执行任务.相当于并行执行
                MapMessage ret=(MapMessage) message;
                //直接把任务交给多线程去处理
                executor.execute(new MessageTask(ret));
            }
            if (message instanceof TextMessage) {
                //处理流程
                //....
            }
        }
    }

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

消费者B

package com.jvm.activemq.bhz.mq;

import com.jvm.util.PropertiesUtil;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ConsumerB {
    public static final String SELECTOR = "receiver='B'";//特别注意此处的写法
    //1.连接工厂
    private ConnectionFactory connectionFactory;
    //2.连接对象
    private Connection connection;
    //3.Session对象
    private Session session;
    //4.生产者对象
    private MessageConsumer messageConsumer;
    //目的地
    Destination destination;

    public ConsumerB() {
        try {
            connectionFactory = new ActiveMQConnectionFactory(
                    ActiveMQConnectionFactory.DEFAULT_USER,
                    ActiveMQConnectionFactory.DEFAULT_PASSWORD,
                    PropertiesUtil.getValue("activemq.properties", "url")
            );
            this.connection = connectionFactory.createConnection();
            connection.start();
            this.session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            this.destination = session.createQueue("first");
            messageConsumer = this.session.createConsumer(destination, SELECTOR);
            System.out.println("ConsumerB ...start");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public void receiver() {
        try {
            this.messageConsumer.setMessageListener(new Listener());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
    class Listener implements MessageListener{
        //创建一个队列
        BlockingQueue queue = new ArrayBlockingQueue<>(1000);
        //创建一个自定义的线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(), //corePoolSize
                20,
                120L,
                TimeUnit.SECONDS,
                queue
        );
        @Override
        public void onMessage(Message message) {
            if (message instanceof MapMessage) {
                //利用线程池开启多个线程去执行任务.相当于并行执行
                MapMessage ret=(MapMessage) message;
                //直接把任务交给多线程去处理
                executor.execute(new MessageTask(ret));
            }
        }
    }

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

处理的任务:(使用Thread.sleep(500)来模拟处理的时间)

package com.jvm.activemq.bhz.mq;

import javax.jms.JMSException;
import javax.jms.MapMessage;
//消息任务,实现了Runable接口
public class MessageTask implements Runnable {

    MapMessage mapMessage;
    public MessageTask(MapMessage ret) {
        this.mapMessage = ret;
    }
    
    @Override
    public void run() {
        try {
            Thread.sleep(500);
            System.out.println("当前线程:"+Thread.currentThread().getName()+"处理任务"+this.mapMessage.getString("id"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

程序的运行结果:消费者A接收到的是偶数(由于版面的原因,省略了部分结果),消费者B接收的是奇数

ConsumerA ...start
当前线程:pool-1-thread-2处理任务2
当前线程:pool-1-thread-1处理任务0
当前线程:pool-1-thread-3处理任务4
当前线程:pool-1-thread-8处理任务14
当前线程:pool-1-thread-5处理任务8
当前线程:pool-1-thread-4处理任务6
当前线程:pool-1-thread-7处理任务12
当前线程:pool-1-thread-9处理任务16
当前线程:pool-1-thread-6处理任务10
当前线程:pool-1-thread-11处理任务20

消费者B:

ConsumerB ...start
当前线程:pool-1-thread-1处理任务1
当前线程:pool-1-thread-2处理任务3
当前线程:pool-1-thread-6处理任务11
当前线程:pool-1-thread-5处理任务9
当前线程:pool-1-thread-3处理任务5
当前线程:pool-1-thread-4处理任务7
当前线程:pool-1-thread-11处理任务21

生产者:

Message send 0
Message send 1
Message send 2
Message send 3
Message send 4
Message send 5
Message send 6
Message send 7
Message send 8
Message send 9
结果: 因为每个任务处理的时间是500毫秒,而100个就是50秒,2个消费者单线程是25秒,而利用多线程技术大约只需要2秒就可以处理完.很好的提高了吞吐量.


你可能感兴趣的:(消息队列)