RabbitMQ

383 阅读8分钟

简介

RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器,支持消息的持久化、事务、拥塞控制、负载均衡等特性,使得RabbitMQ拥有更加广泛的应用场
什么是AMQP?
AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制

RabbitMQ的优点

  1. 开源、性能优秀,稳定性保障
  2. 提供可靠性消息投递模式、返回模式
  3. 与Spring AMQP完美整合,API丰富
  4. 集群模式丰富,表达式配置,HA模式,镜像队列模型
  5. 保证数据不丢失的前提做到高可靠性、可用性

RabbitMQ的应用场景

  1. 异步处理
  2. 流量削峰
  3. 日志处理
  4. 应用解耦

RabbitMQ基础组件

  1. Producer:消息生产者,即发送消息的程序
  2. Consumer:消息消费者,即接受消息的程序
  3. Server:又叫broker,它提供一种传输服务,维护一条从生产者到消费者的路线,保证数据按照指定的方式传输
  4. Connection:连接,应用程序与broker的网络连接
  5. Channel:信道,是进行消息读写的通道,客户端可以建立多个channel,每个channel代表一个会话任务
  6. Message:消息,本质上是一段数据,由Properties和body组成,Properties可以对消息进行修饰(消息的优先级、延迟等高级特性),Body就是消息体内容
  7. Exchange:交换机。接受消息,交换机没有存储功能,根据路由键转发消息到绑定队列,如果没有队列绑定到交换机,那么交换机会直接丢弃消息
  8. Binding Key:Exchange和Queue之间的虚拟连接,用于指定当前队列与交换机Exchange的Key,只要当Routing Key与Bing Key相同的时候,交换机才会根据路由规则分发到匹配的队列Queue中
  9. Routing Key:路由键,生产者将消息发送到交换机Exchange时,一般会指定一个Routing Key,来指定这个消息的路由规则,Routing Key需要与Exchange Type及Binding Key联合使用才能最终生效,将消息发送到指定队列里(Routing Key的长度限制为255Bytes)
  10. Queue:消息队列,保存消息并将消息转发给消费者
  11. Virtual Host:权限控制的基本单元

RabbitMQ的几种队列模型

常见的几种队列模型如下图所示:

通过代码演示一下下面几种队列模型

// 先写一个工具类,用来连接RabbitMQ
public class ConnectionUtil {

    public static Connection getConnection() throws Exception {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 通过工程获取连接
        Connection connection = factory.newConnection();
        return connection;
    }

}

1. 简单队列

生产者将消息发送到队列,消费者从队列中获取消息。

// 消息生产者
public class Producer {

    private final static String QUEUE_NAME = "test_queue";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        // 从连接中创建通道
        Channel channel = connection.createChannel();

        // 声明(创建)队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 消息内容
        String message = "Hello World  !!!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" Producer Sent :" + message );

        //关闭通道和连接
        channel.close();
        connection.close();
    }
}

// 消息消费者
public class Consumer {

    private final static String QUEUE_NAME = "test_queue";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列
        channel.basicConsume(QUEUE_NAME, true, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer Received:" + message );
        }
    }
}

运行消息消费者、消息生产者,我们来看控制台输出:

Producer Sent :Hello World  !!!
Consumer Received:Hello World  !!!

2. 工作模式

我们定义一个消息生产者,两个消息消费者

public class Producer {

    private final static String QUEUE_NAME = "work_queue";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        for (int i = 0; i < 20; i++) {
            // 消息内容
            String message = "" + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println(" Producer Sent :  " + message );
            // 设置一个时间间隔
            Thread.sleep(i * 10);
        }

        channel.close();
        connection.close();
    }
}
// Consumer1
public class Consumer1 {

    private final static String QUEUE_NAME = "work_queue";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer1 Received :  " + message );
            //休眠10毫秒
            Thread.sleep(10);
            // 返回确认状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}
// Consumer2
public class Consumer2 {

    private final static String QUEUE_NAME = "work_queue";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成状态
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer2 Received :  " + message );
            // 休眠1秒
            Thread.sleep(1000);
            // 手动确认消息状态
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

运行Consumer1、Consumer2,再运行Producer,我们来看控制台输出:

// Producer
 Producer Sent :  0
 Producer Sent :  1
 Producer Sent :  2
 ......
 Producer Sent :  19

// Consumer1
 Consumer1 Received :  0
 Consumer1 Received :  2
 Consumer1 Received :  3
 Consumer1 Received :  4
 Consumer1 Received :  5
 Consumer1 Received :  6
 Consumer1 Received :  7
 Consumer1 Received :  8
 Consumer1 Received :  9
 Consumer1 Received :  10
 Consumer1 Received :  11
 Consumer1 Received :  12
 Consumer1 Received :  13
 Consumer1 Received :  14
 Consumer1 Received :  15
 Consumer1 Received :  17
 Consumer1 Received :  18
 Consumer1 Received :  19

// Consumer2
 Consumer2 Received :  1
 Consumer2 Received :  16

消息的确认模式

// true:自动确认  false:手动确认
channel.basicConsume(QUEUE_NAME, false, consumer);

// 手动确认消息状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

自动确认 只要消息从队列中获取,无论消费者获取到消息后是否成功消息,都认为是消息已经成功消费 手动确认 消费者从队列中获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态

3. 发布/订阅模式

  1. 1个生产者,多个消费者
  2. 每一个消费者都有自己的一个队列
  3. 生产者没有将消息直接发送到队列,而是发送到了交换机
  4. 每个队列都要绑定到交换机
  5. 生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的

一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费

// 消息生产者
public class Producer {

    private final static String EXCHANGE_NAME = "test_fanout";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange 交换机类型fanout
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        // 消息内容
        String message = "Hello World !!!";
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println("Producer Sent : " + message );

        channel.close();
        connection.close();
    }
}
// 消息消费者
public class Consumer {

    private final static String QUEUE_NAME = "ps_queue";

    private final static String EXCHANGE_NAME = "test_fanout";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer1 Received: " + message);
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

控制台输出:

//消息生产者
Producer Sent : Hello World !!!

//只有Consumer1接受到了消息
Consumer1 Received: Hello World !!!

4. 路由模式

public class Producer {

    private final static String EXCHANGE_NAME = "test_direct";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        // 消息内容
        String message = "Hello World !!!";
        // routing key是key1
        channel.basicPublish(EXCHANGE_NAME, "key1", null, message.getBytes());
        System.out.println(" Producer Sent : " + message);

        channel.close();
        connection.close();
    }
public class Consumer1 {

    private final static String QUEUE_NAME = "routing_queue";

    private final static String EXCHANGE_NAME = "test_direct";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key1");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer1 Received: " + message);
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}
public class Consumer2 {

    private final static String QUEUE_NAME = "routing_queue";

    private final static String EXCHANGE_NAME = "test_direct";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key2");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer2 Received : " + message);
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

控制台输出:

//消息生产者
Producer Sent : Hello World !!!

//只有Consumer1接受到了消息
Consumer1 Received: Hello World !!!

5. 主题模式

主题模式也称通配符模式:* 代表一个; # 代表一个或多个

// 消息生产者
public class Producer {

    private final static String EXCHANGE_NAME = "test_topic";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "key.1", null, message.getBytes());
        System.out.println(" Producer Sent : " + message );

        channel.close();
        connection.close();
    }
}
public class Consumer1 {

    private final static String QUEUE_NAME = "topic_queue";

    private final static String EXCHANGE_NAME = "test_topic";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.1");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer1 Received:" + message);
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}
public class Consumer2 {

    private final static String QUEUE_NAME = "topic_queue";

    private final static String EXCHANGE_NAME = "test_topic";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key.*");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" Consumer2 Received:" + message);
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

控制台输出:

// 消息生产者
Producer Sent : Hello World !!!

//  只有Consumer1接受到了消息
Consumer1 Received: Hello World !!!