RabbitMQ 基础知识总结

RabbitMQ 知识总结

RabbitMQ 是一个开源的消息代理(message broker)系统,最初由 Rabbit Technologies Ltd 开发,并在开源社区的支持下不断发展和完善。它提供了强大的消息传递机制,被广泛用于构建分布式系统和应用。本博客将深入探讨 RabbitMQ 的各个方面,以便读者更全面地了解和使用这一重要的消息代理系统。

1. RabbitMQ 简介

RabbitMQ 是一种高性能、开源、跨平台的消息代理系统,它实现了高效的消息传递机制,允许不同的应用程序通过消息进行通信。RabbitMQ 支持多种消息传递模式,包括点对点、发布/订阅和路由等,使其成为构建分布式系统和微服务架构的理想选择。

2. 消息队列基础

消息队列是一种通信方式,允许不同的应用程序或服务之间通过异步消息传递进行通信。它们通常包括生产者、消息队列和消费者。生产者负责创建和发送消息,消息队列负责存储消息,消费者负责接收并处理消息。

3. RabbitMQ 的核心概念

3.1. 队列(Queues)
队列是 RabbitMQ 存储消息的地方。生产者将消息发送到队列,消费者从队列中接收消息。队列可以看作是消息的缓冲区,确保消息能够按顺序传递。

3.2. 交换机(Exchanges)
交换机是消息的分发中心,负责将消息路由到一个或多个队列。RabbitMQ 提供了不同类型的交换机,如直接交换机、扇出交换机、主题交换机等,以支持不同的消息传递模式。

3.3. 绑定(Bindings)
绑定定义了交换机如何将消息路由到队列。它将交换机与队列之间建立关联关系,确保消息能够被正确地路由到目标队列。

4. 消息的发布和消费

在 RabbitMQ 中,生产者使用 Exchange 将消息发布到队列,而消费者从队列中获取消息并进行处理。这个过程包括消息的发布、路由和消费。

5. 消息确认与持久化

为了确保消息不会丢失,RabbitMQ 提供了消息确认机制和消息持久化选项。消息确认确保消息被成功处理,而消息持久化则将消息存储在磁盘上,以便在代理重启时不会丢失。

5.1消息的可靠性投递

RabbitMQ消息投递的路径为:

生产者 —> 交换机 —> 队列 —> 消费者

在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?有三种可靠性投的机制:

  • 确认模式(confirm)可以监听消息是否从生产者成功传递到交换机。
spring:
 rabbitmq:
   host: 192.168.0.162
   port: 5672
   username: cvdf
   password: cvdf
   virtual-host: /cvdf
    # 开启确认模式
   publisher-confirm-type: correlated
  • 退回模式(return)可以监听消息是否从交换机成功传递到队列。
spring:
  rabbitmq:
    host: 192.168.184.136
    port: 5672
    virtual-host: /cvdf
    username: cvdf
    password: cvdf
    # 开启确认模式
    publisher-confirm-type: correlated
     # 开启回退模式
    publisher-returns: true
  • 消费者消息确认(Consumer Ack)可以监听消费者是否成功处理消息

在RabbitMQ中,消费者接收到消息后会向队列发送确认签收的消息,只有确认签收的消息才会被移除队列。这种机制称为消费者消息确认(Consumer Acknowledge,简称Ack)。类似快递员派送快递也需要我们签收,否则一直存在于快递公司的系统中。

消息分为自动确认和手动确认。自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除。但是在实际开发中,收到消息后可能业务处理出现异常,那么消息就会丢失。此时需要设置手动签收,即在业务处理成功再通知签收消息,如果出现异常,则拒签消息,让消息依然保留在队列当中。

1. 自动确认:spring.rabbitmq.listener.simple.acknowledge=“none”
2. 手动确认:spring.rabbitmq.listener.simple.acknowledge="manual

spring:
  rabbitmq:
    host: 192.168.184.136
    port: 5672
    virtual-host: /cvdf
    username: cvdf
    password: cvdf
    # 手动签收消息
    listener:
      simple:
        acknowledge-mode: manual

6. 消息传递模式

RabbitMQ 支持多种消息传递模式,包括简单队列模式、发布/订阅模式和路由模式。每种模式都适用于不同的应用场景,允许灵活的消息传递。

6.1. 简单队列模式
简单队列模式是最基本的模式,将消息发送到一个队列,一个消费者从队列中获取消息并处理。适用于点对点通信。
特点:

  1. 一个生产者对应一个消费者,通过队列进行消息传递。
  2. 该模式使用direct交换机,direct交换机是RabbitMQ默认交换机。
/**
 * 简单模式生产者
 */
public class Producer {
    @SneakyThrows
    public static void main(String[] args) {
        //1.创建链接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.184.136");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("cvdf");
        connectionFactory.setPassword("cvdf");
        connectionFactory.setVirtualHost("/cvdf");
        //2.创建链接
        Connection connection = connectionFactory.newConnection();
        //3.创建信道
        Channel channel = connection.createChannel();
        //4.创建队列,如果队列存在,则使用
        /**
         * 参数1:队列名
         * 参数2:是否持久化,true表示MQ重启后队列还在
         * 参数3:是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问
         * 参数4:是否自动删除,true表示不在使用队列时自动删除队列
         * 参数5:其他额外参数
         */
        channel.queueDeclare("simple_queue",false,false,false,null);
        //5.发送消息
        /**
         * 参数1:交换机名 ""表示默认交换机
         * 参数2:路由键,简单模式就是队列名
         * 参数3:其他额外参数
         * 参数4:要传递的消息字节数组
         */
        String message = "hello simple message mq!!!";
        channel.basicPublish("","simple_queue",null,message.getBytes());
        //6.关闭信道和连接
        channel.close();
        connection.close();
        System.out.println("发送成功");

    }
}

6.2. 发布/订阅模式
发布/订阅模式允许多个消费者订阅同一个队列,并同时接收消息。适用于广播通信。
在开发过程中,有一些消息需要不同消费者进行不同的处理,如电商网站的同一条促销信息需要短信发送、邮件发送、站内信发送等。此时可以使用发布订阅模式(Publish/Subscribe)

特点:

  1. 生产者将消息发送给交换机,交换机将消息转发到绑定此交换机的每个队列中。
  2. 工作队列模式的交换机只能将消息发送给一个队列,发布订阅模式的交换机能将消息发送给多个队列。
  3. 发布订阅模式使用fanout交换机。
/**
 * 发布订阅生产者
 */
public class Producer {
    @SneakyThrows
    public static void main(String[] args) {
        //1.创建链接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.184.136");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("cvdf");
        connectionFactory.setPassword("cvdf");
        connectionFactory.setVirtualHost("/cvdf");
        //2.创建链接
        Connection connection = connectionFactory.newConnection();
        //3.创建信道
        Channel channel = connection.createChannel();
        //4.创建交换机
        /**
         * 参数1:交换机名
         * 参数2:交换机类型
         * 参数3:交换机持久化
         */
        channel.exchangeDeclare("exchange_fanout", BuiltinExchangeType.FANOUT,true);
        //5.创建队列,如果队列存在,则使用
        /**
         * 参数1:队列名
         * 参数2:是否持久化,true表示MQ重启后队列还在
         * 参数3:是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问
         * 参数4:是否自动删除,true表示不在使用队列时自动删除队列
         * 参数5:其他额外参数
         */
        channel.queueDeclare("send_email",true,false,false,null);
        channel.queueDeclare("send_message",true,false,false,null);
        channel.queueDeclare("send_station",true,false,false,null);
        //6.交换机绑定队列
        /**
         * 参数1:队列名
         * 参数2:交换机名
         * 参数3:路由关键字 ,发布订阅模式写""即可
         */
        channel.queueBind("send_email","exchange_fanout","");
        channel.queueBind("send_message","exchange_fanout","");
        channel.queueBind("send_station","exchange_fanout","");
        //7.发送消息
        /**
         * 参数1:交换机名 ""表示默认交换机
         * 参数2:路由键,简单模式就是队列名
         * 参数3:其他额外参数 ,这里表示消息为持久化消息,既除了保存内存还会保存到磁盘中
         * 参数4:要传递的消息字节数组
         */
        for (int i = 1; i <=10 ; i++) {
            channel.basicPublish("exchange_fanout",
                    "",
                    null,
                    ("你好,尊敬的用户,秒杀商品开抢了"+i).getBytes());
        }
        //8.关闭信道和连接
        channel.close();
        connection.close();
        System.out.println("发送成功");

    }
}

6.3. 路由模式
路由模式通过交换机和绑定来控制消息的路由方式,允许根据消息的特性将消息发送到不同的队列。适用于灵活的消息分发。使用发布订阅模式时,所有消息都会发送到绑定的队列中,但很多时候,不是所有消息都无差别的发布到所有队列中。比如电商网站的促销活动,双十一大促可能会发布到所有队列;而一些小的促销活动为了节约成本,只发布到站内信队列。此时需要使用路由模式(Routing)完成这一需求。

特点:

  1. 每个队列绑定路由关键字RoutingKey
  2. 生产者将带有RoutingKey的消息发送给交换机,交换机根据RoutingKey转发到指定队列。路由模式使用direct交换机。
/**
 * 路由模式:消息生产者
 */
public class 
{
    @SneakyThrows
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.184.136");
        factory.setPort(5672);
        factory.setVirtualHost("/cvdf");
        factory.setUsername("cvdf");
        factory.setPassword("cvdf");
        //2.创建连接
        Connection connection = factory.newConnection();
        //3.创建信道
        Channel channel = connection.createChannel();
        //4.声明交换机
        /**
         * 参数1:交换机名
         * 参数2:交换机类型
         * 参数3:是否持久化
         */
        channel.exchangeDeclare("exchange_direct", BuiltinExchangeType.DIRECT,true);
        //5.声明队列
        /**
         * 参数1:队列名
         * 参数2:吃否持久化
         * 参数3:是否私有
         * 参数4: 是否自动删除
         * 参数5:其他参数
         */
        channel.queueDeclare("routing_queue1",true,false,false,null);
        channel.queueDeclare("routing_queue2",true,false,false,null);

        //6.绑定routKey
        /**
         * 参数1:队列名
         * 参数2:交换机名
         * 参数3:routeKey 路由模式routeKey必须指定,只能指定单词,多个单词用.隔离
         */
        channel.queueBind("routing_queue1","exchange_direct","info");
        channel.queueBind("routing_queue2","exchange_direct","info");
        channel.queueBind("routing_queue2","exchange_direct","error");

        //7.发送消息
        /**
         * 参数1:交换机名
         * 参数2:路由键,发送到指定路由key
         * 参数3:其他额外参数 ,这里表示消息为持久化消息,既除了保存内存还会保存到磁盘中
         * 参数4:要传递的消息字节数组
         */
        channel.basicPublish("exchange_direct","info",null,"info消息".getBytes());
        channel.basicPublish("exchange_direct","error",null,"error消息".getBytes());
        //8.关闭信道和连接
        channel.close();
        connection.close();
        System.out.println("发送成功");


    }
}

6.3通配符模式

通配符模式(Topic)是在路由模式的基础上,给队列绑定带通配符的路由关键字,只要消息的RoutingKey能实现通配符匹配,就会将消息转发到该队列。通配符模式比路由模式更灵活,使用topic交换机。

通配符规则:

  1. 消息设置RoutingKey时,RoutingKey由多个单词构成,中间以 . 分割。
  2. 队列设置RoutingKey时, # 可以匹配任意多个单词, * 可以匹配任意一个单词。
/**
 * 通配符模式:消息生产者
 */
public class TopicProducer {
    @SneakyThrows
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.184.136");
        factory.setPort(5672);
        factory.setVirtualHost("/cvdf");
        factory.setUsername("cvdf");
        factory.setPassword("cvdf");
        //2.创建连接
        Connection connection = factory.newConnection();
        //3.创建信道
        Channel channel = connection.createChannel();
        //4.声明交换机:交换机名字、类型、是否持久化
        channel.exchangeDeclare("exchange_topic", BuiltinExchangeType.TOPIC,true);
        //5.声明队列:队列名,是否持久化,是否私有,是否自动删除,额外信息
        channel.queueDeclare("topic_queue1",true,false,false,null);
        channel.queueDeclare("topic_queue2",true,false,false,null);
        //6.绑定关系
        channel.queueBind("topic_queue1","exchange_topic","*.goods.*");
        channel.queueBind("topic_queue1","exchange_topic","*.adv.*");
        channel.queueBind("topic_queue2","exchange_topic","#.goods.#");

        //7.发送消息
        channel.basicPublish("exchange_topic","huawei.goods.phone",null,"华为手机商品信息".getBytes());
        channel.basicPublish("exchange_topic","huawei.adv.phone",null,"华为手机广告信息".getBytes());

        //8.关闭资源
        channel.close();
        connection.close();

    }
}

以上四种生产者对应的消费者都可以由以下消费者改造,不在重复编写

#消费者代码
public class Consumer {
    @SneakyThrows
    public static void main(String[] args) {
        //1.创建链接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.184.136");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("psjj");
        connectionFactory.setPassword("psjj");
        connectionFactory.setVirtualHost("/psjj");
        //2.创建链接
        Connection connection = connectionFactory.newConnection();
        //3.创建信道
        Channel channel = connection.createChannel();
        //4.监听队列
        /**
         * 参数1:监听的队列名
         * 参数2:是否自动签收,如果设置为false,则需要手动确认消息已接收,否则MQ会一直发送消息
         * 参数3:Consumer的实现类,重写该类方法表示接受到消息后如何消费
         */
        channel.basicConsume("simple_queue", true, 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("接收消息,消息为:"+message);
            }
        });
    }
}

以上就是RabbitMQ的基础概念、核心特性的知识总结,RabbitMQ 的高级特性、应用场景、集成与安全性等方面由下一篇RabbitMQ 的高级特性陈述,如有需要请移步下一篇文章。
以上就是全部内容,如果你有任何问题、意见或建议,都欢迎在评论中分享。让我们继续分享知识,共同成长,一起走向更加美好的未来。感谢你们的阅读,祝愿你们在未来的道路上一帆风顺!

你可能感兴趣的:(rabbitmq,分布式)