本文将从三个方便了解的RabbitMQ

1,RabbitMQ的是什么

2,为什么要有的RabbitMQ 

3,RabbitMQ的五种队列的实现

一,RabbitMQ的是什么

首先在了解RabbitMQ之间我们要知道什么是MQ,MQ的全称为Message Queue也就是消息队列,是一种应用程序之间的通信方法 ;应用程序通过读写消息队列的消息(针对应用程序的数据)来通信,而无需专门用连接来链接它们。

二,为什么要有的RabbitMQ

由于高并发环境下数据来不及同步处理,请求往往会发生阻塞;比如大量的插入,更新等语句同时到达db,直接导致行锁,表锁,甚至最后请求会堆积过多,触发太多连接的错误;而RabbitMQ的是异步的消息,可以很好的解决高并发环境下的种种问题。

使用场景:一些无需及时报道查看御姐耗时的操作,使用MQ来处理,这样的话可以大大节省服务器的请求响应时间,从而提高系统的吞吐量。

三,RabbitMQ的五种队列的实现

1,Simple Queue(简单队列)

消息发送者

package com.vcredit.jdr.rabbitmq.project.send;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.vcredit.jdr.rabbitmq.project.config.RabbitMQConfig;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class SendMessage {

    private static final String QUEUE_NAME = "queue_simple";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("127.0.0.1");
            connection = factory.newConnection();
            channel = connection.createChannel();

            channel.queueDeclare(
                    QUEUE_NAME, // 队列的名称
                    false, // 是否为持久化队列,若为true,服务器关闭后次队列依然处在
                    false, // 是否为独占队列(仅限于此连接),true为是
                    false, // 是否自动删除(服务器不适用其时删除次队列),true为是
                    null // 队列的其他属性(构造参数)
            );
            String message = "[1] Hello World";
            channel.basicPublish(
                    "", // 消息发布到的交换器名
                    QUEUE_NAME, // 路由秘钥
                    null, // 消息的其他属性 - 路由头等
                    message.getBytes() // 消息信息(消息主体)
            );
            System.err.println("[1] success, send message: " + message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            channel.close();
            connection.close();
        }
    }

}

消息接收者

package com.vcredit.jdr.rabbitmq.project.receive;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReceiveMessage {

    private static final String QUEUE_NAME = "queue_simple";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.err.println("[1] waiting for message...");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.err.println("[1] success,received message: " + message);
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

运行ReceivedMessage显示消费者正在等待消息

消息队列 RabbitMQ处理消息慢 rabbitmq消息队列类型_java

运行SendMessage函数显示成功发送了一条消息

消息队列 RabbitMQ处理消息慢 rabbitmq消息队列类型_消息队列 RabbitMQ处理消息慢_02

让我们回ReceivedMessage发现,成功接收了一条消息

消息队列 RabbitMQ处理消息慢 rabbitmq消息队列类型_RabbitMQ_03

这就是简单队列,一个发送者对应一个消费者,当然多个发送者与多个消费者肯定是可以的; 这里建议读者多写几个发送者与消费者来测试效果,我这里就不一一列举了

因为一下的几个队列都需要创建连接,频道对象,所以我们稍作封装

RabbitMQUtil

package com.vcredit.jdr.rabbitmq.project.utils;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class RabbitMQUtil {

    public static final String HOST = "127.0.0.1";

    public static Connection getConnection(String host) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(host);
        return factory.newConnection();
    }

    public static Channel getChannel(String host) throws IOException, TimeoutException {
        Connection connection = getConnection(host);
        return connection.createChannel();
    }

    public static void closeAll(Channel channel, Connection connection) throws IOException, TimeoutException {
        if (channel != null) channel.close();
        if (connection != null) connection.close();
    }
}

2,Work Queue(工作队列)

上面的简单队列只能简单的发送与接收消息,而不能很好的分发消息;例如有一个消息发送者发送了50条消息,接收者1的处理能力差些,接收者2的处理能力好些,这时你会发现一会后接收者2处理完了消息后,接收者1还在工作;这样的话一个很忙一个很闲,服务器的资源不能很好的分配;这样该怎么办这时候我们可以使用工作队列来处理类似问题

队列工作的主要思想的英文避免某一个消费者执行资源密集型任务,并且必须等待它完成,而其他消费者少任务或无任务执行(这里有一个机制,RabbitMQ的情况默认下的英文轮询配给物任务,也就是说如果有2个消费者,50条消息,每个消费者会执行25条消息,而不会管消息执行的繁琐程度)

发送者

package com.vcredit.jdr.rabbtmq.project.task;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import com.vcredit.jdr.rabbitmq.project.config.RabbitMQConfig;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class NewTask {

    private static final String QUEUE_NAME = "task";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = RabbitMQUtil.getConnection(RabbitMQConfig.MQ_HOST);
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        // 发送多条消息
        for (int i = 0; i < 50; i++) {
            String message = "send message: " + i;
            channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
            System.out.println("success, send message: " + message);
        }

        RabbitMQUtil.closeAll(channel, connection);
    }
}

接收者1
package com.vcredit.jdr.rabbtmq.project.task;

import com.rabbitmq.client.*;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReceiveTask {

    private static final String QUEUE_NAME = "task";

    public static void main(String[] args) throws TimeoutException, IOException {
        Connection connection = RabbitMQUtil.getConnection(RabbitMQUtil.HOST);
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//        channel.basicQos(1); // 设置basicQos=1,每次发完消息后告诉队列自己已经发完了,队列就会给自己在发送一条消息
        System.err.println("[1] wait message ...");

        final Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("[1] Received1" + message);
                try {
                    Thread.sleep(1000); // 使用线程休眠模拟任务的复杂程度
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] Done");
//                    channel.basicAck(
//                            envelope.getDeliveryTag(), // 收到的标签{@link com.rabbitmq.client.AMQP.Basic.GetOk}或{@link com.rabbitmq.client.AMQP.Basic.Deliver}
//                            false // 自动回执,true为是;收到消息后告诉消息发送者已经收到消息
//                    );
                }
            }
        };
        channel.basicConsume(
                QUEUE_NAME, // 队列名称
                // 是否自动应答
                // true为是,一旦RabbitMQ将消息发送给了消费者就会从内存中删除;如果杀死正在执行消息的消费者那么数据就会丢失
                // 若为false,消费者挂了后会将此消息交给另外一个消费者执行,收到消息后告诉消息发送者已经收到消息,并删除内存中的消息
                true,
                consumer
        );
    }
    
}

接收者2
package com.vcredit.jdr.rabbtmq.project.task;

import com.rabbitmq.client.*;
import com.vcredit.jdr.rabbitmq.project.utils.RabbitMQUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReceiveTask2 {

    private static final String QUEUE_NAME = "task";

    public static void main(String[] args) throws TimeoutException, IOException {
        Connection connection = RabbitMQUtil.getConnection(RabbitMQUtil.HOST);
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//        channel.basicQos(1);
        System.err.println("[2] wait message ...");

        final Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("[2] Received" + message);
                try {
                    Thread.sleep(3000); // 使用线程休眠模拟任务的复杂程度
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("Done2");
//                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }

}


 


分别执行接收者1、接收者2、发送者,你会发现每个接收者处理了25条消息,且接收者1处理速度要比接收者2快;接下来我们改造上面代码,使队列公平的遣派任务。

其实很简单,我们只需要将两个消费者的注释去掉,将最后一行的channel.basicConsume(QUEUE_NAME,true,consume);的第二个参数给为假即可;然后我们会惊奇的发现接收者1与接收者2基本是同一时间完成处理,而他们处理消息的数量也不一样了,这样我就解决了Simple Queue解决不了的问题了