RabbitMQ

RabbitMQ

P21

1、MQ基本概念

1.1 基本概念

MQ全称Message Queue(消息队列),是在消息的传输过程中保存信息的容器,存储消息的中间件。多用于分布式系统之间进行通信。

image-20220225103605288

分布式系统通信的两种方式:直接远程调用和借助第三方完成间接通信

发送方称为生产者,接收方称为消费者。

1.2 MQ的优势和劣势:

MQ优势:

  • 应用解耦:增加系统容错性和可维护性

    直接远程调用:系统的耦合性越高,容错性就越低,可维护性就越低。

    image-20220225111221101

    采用MQ后使得应用间解耦,提升容错性和可维护性。

    image-20220225111344169
  • 异步提速:提高用户体验和系统吞肚量

    200ms以内用户是无感知的

    image-20220225111435169 image-20220225111624787
  • 削峰填谷:提高系统稳定性

    image-20220225111833097

    使用MQ之后,限制消费信息的速度为1000,这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被“削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费信息的熟读还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。

    image-20220225111856890

劣势

  • 系统可用性降低

    系统引入的外部依赖越多,系统稳定性就越差。一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用?

  • 系统复杂度提高

    MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?如何保证消息传递的顺序性?

  • 一致性问题

    A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功、D系统处理失败。如何保证消息数据处理的一致性?

image-20220225112510881

1.3 什么时候使用MQ

既然MQ有优势也有劣势,那么使用MQ需要满足什么条件呢?

1. 生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没做,上层却当成动作做完了继续往后走,既所谓的异步成为了可能。
2. 容许短暂的不一致性
3. 确实使用了有效果。既解耦、提速,削峰这些方面的收益,超过加入MQ,管理MQ这些成本。

1.4 常见的MQ产品

image-20220225115723899

image-20220225115735728

1.5 RabbitMQ简介

image-20220225120208050

分发也可以称为路由

image-20220225120345041

image-20220225120814385

image-20220225120854908

image-20220225120948936

image-20220225121229420

RabbitMQ的安装和配置

运行docker命令:

docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management

访问http://localhost:15672/

输入账户名和密码:默认Guest

2、RabbitMQ的入门程序

2.1创建一个简单模式

2.1.1生产者

  1. 新建RabbitMQ解决方案

  2. 创建RabbitMQClient项目

  3. 引入RabbitMQ.Client依赖

  4. 编写代码如下

//1.创建连接工厂
var factory = new ConnectionFactory()
{
    HostName = "localhost",
    Port = 5672,
    VirtualHost = "/",
    UserName = "guest",
    Password = "guest"
};
//2.创建连接
using (var connection = factory.CreateConnection())
//3.创建channel
using (var channel = connection.CreateModel())
{
    //4.创建队列
    /*QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object> arguments);
                 * 参数:
                 *      1. queue:队列名称
                 *      2. durable:是否持久化,当mq重启之后,还在
                 *      3. exclusive:
                 *          是否独占。只能有一个消费者监听这队列
                 *          当Connection关闭时,是否删除队列
                 *      4. autoDelete:是否自动删除。当没有Consume时,自动删除掉
                 *      5. arguments:   参数。
                 */
    //如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
    channel.QueueDeclare(queue: "hello_world",
                         durable: false,
                         exclusive: false,
                         autoDelete: false,
                         arguments: null);
    //5.发送消息

    string message = "Hello RabbitMQ~~~~!";
    var body = Encoding.UTF8.GetBytes(message);

    /*
                 *  BasicPublish(this IModel model, string exchange, string routingKey, IBasicProperties basicProperties, ReadOnlyMemory<byte> body);
                 *  参数:
                 *         1. model:channel
                 *         2. exchange:交换机名称。简单模式下交换机会使用默认的""
                 *         3. routingKey:路由名称。如果使用默认交换机,routingKey=队列名称
                 *         4. basicProperties:配置信息
                 *         5. body:发送消息数据 字节数据
                 */
    channel.BasicPublish(exchange: "",
                         routingKey: "hello_world",
                         basicProperties: null,
                         body: body);

    //6.释放资源    

}
  1. 启动项目:

    可以看到dashboard中有个hello_world的队列
    image-20220227163014073

2.1.2消费者

  1. 创建RabbitMQConsumer项目
  2. 引入RabbitMQ.Client依赖
  3. 和生产者代码对比
//5.接收消息
var consumer = new EventingBasicConsumer(channel);
/*
                 * 回调方法:当接收到信息后,会自动执行该方法。
                 * BasicDeliverEventArgs(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, ReadOnlyMemory<byte> body);
                 *      参数:
                 *          1. consumerTag:标识
                 *          2. deliveryTag
                 *          3. redelivered
                 *          4. exchange:交换机
                 *          5. routingKey
                 *          6. properties:配置信息
                 *          7.body:数据
                 */
consumer.Received += (model, ea) =>
{
    var body = ea.Body.ToArray();
    var message = Encoding.UTF8.GetString(body);
    Console.WriteLine(" [x] Received {0}", message);
    Console.WriteLine($"consumerTag:{ea.ConsumerTag}");
    Console.WriteLine($"deliveryTag:{ea.DeliveryTag}");
    Console.WriteLine($"redelivered:{ea.Redelivered}");
    Console.WriteLine($"exchange:{ea.Exchange}");
    Console.WriteLine($"routingKey:{ea.RoutingKey}");
    Console.WriteLine($"properties:{ea.BasicProperties}");
};

/*
                * BasicConsume(this IModel model, string queue, bool autoAck, IBasicConsumer consumer);
                *   参数:
                *          1.queue:队列
                *          2.autoAck:是否自动确认
                *          3.consumer:回调对象
                 */
channel.BasicConsume(queue: "hello_world",
                     autoAck: true,
                     consumer: consumer);

Console.WriteLine("按下enter键退出");
Console.ReadLine();

image-20220227191232811

3、RabbitMQ的工作模式

工作模式:指的是消息路由的分发机制

3.1 Work Queue工作队列的模式

image-20220227204110558

Work Queues:与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息。

应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。

代码实现

  1. 生产者循环发送十条数据
for (int i = 0; i < 10; i++)
{
    string message = "Hello RabbitMQ_WorkQueue~~~~!" + i;
    var body = Encoding.UTF8.GetBytes(message);

    channel.BasicPublish(exchange: "",
                         routingKey: "Work_Queue",
                         basicProperties: null,
                         body: body);
}
2. 消费者增加一个消费者。消费者轮询消费信息

测试

image-20220227210825839

image-20220227212505033

3.2 PubSub 订阅模式

订阅模式:一条消费被多个消费者同时消费。

image-20220227212611142

在订阅模型中,多了一个Exchange角色,而且过程略有变化:

  • P:生产者,也就是要发送消费的程序,但是不在发送到队列中,而是发给x(交换机)。

  • C:消费者,消息的接收者,会一直等待消息到来。

  • Queue:消息队列,接收消息,缓存消息

  • Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列,或是将消息丢弃。到底如何操作,取决于Exchange的类型,Exchange有常见以下三种类型

    • Fanout:广播,将消息交给所有绑定到交换机的队列
    • Direct:定向,把消息交给符合指定Routing key的队列
    • Topic:通配符,将消息交给符合Routing Pattern(路由模式)的队列

    Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队则,那么消息会丢失。

4、CAP整合RabbitMQ

5、高级

5.1 消息可靠性投递_confirm

image-20220227235949251

5.2 消息可靠性投递_return

5.3 消息可靠性投递_Consumer_ACK

image-20220228001142211

5.4 消费端限流

5.5 TTL

image-20220228003006604

5.6 死信队列

image-20220228003505493

image-20220228003521552

image-20220228003708060

5.6.1死信队列概述

5.6.1死信队列代码实现

5.7延迟队列

image-20220228003940433

image-20220228004048029

5.7.1 概述

5.7.2 代码实现

5.8 日志监控

5.9 消息追踪

image-20220228004210570

image-20220228004239610

5.10 消息补偿

image-20220228004346546

5.11 幂等性保障

image-20220228004729038

image-20220228005002962

5.12 镜像队列

5.13 集群搭建_haproxy

参考资料

黑马程序员RabbitMQ全套教程视频

CAP官方中文文档

RabbitMQ跟CAP组件简单入门

CAP官方视频入门

posted @ 2022-02-26 22:15  CCmonitor  阅读(118)  评论(0)    收藏  举报