C#队列学习笔记:RabbitMQ基础知识
一、引言
rabbitmq是rabbit message queue的简写,但不能仅仅理解其为消息队列,消息代理更合适。rabbitmq是一个由 erlang 语言开发的amqp(高级消息队列协议)的开源实现,其内部结构如下:
rabbitmq作为一个消息代理,主要和消息打交道,负责接收并转发消息。rabbitmq提供了可靠的消息机制、跟踪机制和灵活的消息路由,支持消息集群和分布式部署。适用于排队算法、秒杀活动、消息分发、异步处理、数据同步、处理耗时任务、cqrs等应用场景。
二、基础概念
讲解基础概念之前,先来构造一个整体结构图,以方便我们更好地去理解rabbitmq的基本原理。
通过上面这张结构图,能够清晰地了解到send message到receive message的一个大致流程。
2.1、queue
queue(队列)在rabbitmq中的作用是存储消息,队列的特性是先进先出。上图可以清晰地看到client a和client b是生产者,生产者生产消息最终被送到rabbitmq的内部对象queue中去,而消费者则是从queue中取出数据,可以简化表示为:
生产者send message,“a”被传送到queue中,消费者发现消息队列queue中有订阅的消息,就会将这条消息a读取出来进行一系列的业务操作。这里是一个消费者对应一个队列queue,也可以多个消费者订阅同一个队列queue,此时就会将queue里面的消息平分给其他的消费者,但是会存在一个问题就是如果每个消息的处理时间不同,就会导致某些消费者一直在忙碌中,而有的消费者处理完消息后一直处于空闲状态。这时我们可以使用prefetchcount来限制每次发送给消费者消息的个数,详情见下图所示:
这里的prefetchcount=1是指每次从queue中发送一条消息,等消费者处理完这条消息后,queue会再发送一条消息给消费者。
2.2、exchange
在本节开头的结构图中我们留下了一个坑:消费者client a和消费者client b,是如何知道要发送的消息是给queue1还是给queue2的呢?下面让我们来解开这个面纱。首先要明确的一点是:生产者生产的消息并不是直接发送给消息队列queue的,而是要经过exchange(交换器)将消息路由到一个或多个queue,当然这里还会对不符合路由规则的消息进行丢弃,这个涉及到后面要讲的exchange type。那么exchange是怎样将消息准确地推送到对应的queue的呢?功劳最大的当属binding。rabbitmq通过binding将exchange和queue链接在一起,这样exchange就知道如何将消息准确地推送到queue中去了,简单示意图如下所示:
生产者将消息发送给exchange的时候,一般会产生一个routing key,而通过binding链接exchange和queue的时候,一般会指定一个binding key。当routing key与binding key对应得上的时候,消息就会发送到对应的queue中去。
2.3、exchange type
exchange type有4种,不同的类型有着不同的策略,也就意味着类型的不同将决定着绑定的queue的不同。换言之,生产者发送了一个消息,routing key的规则是a,那么生产者会将routing key=a的消息推送到exchange中,这时候exchange根据自己的规则去筛选生产者发来的消息。
- fanout
fanout类型的exchange,路由规则非常简单:它会把所有发送到该exchange的消息,路由到所有与它绑定的queue中。
上图所示,生产者(p)将消息1推送到exchange,由于exchange type=fanout,这时候会遵循fanout的规则将消息推送到所有与它绑定的queue中(上图的两个queue),最后给两个消费者消费。
- direct
direct类型的exchange,路由规则也很简单:它会把消息路由到那些routing key与binding key完全匹配的queue中。
当生产者(p)发送消息时rotuing key=booking,exchange获取到生产者发送过来的消息后,会根据自身的规则进行匹配相应的queue,这时发现queue1和queue2都符合,就会将消息传送给这两个队列。如果我们以rotuing key=create和rotuing key=confirm发送消息时,这时消息只会被推送到queue2队列中,其它routing key的消息将会被丢弃。
- topic
如果说前面的direct规则是严格意义上的匹配(即routing key必须与binding key相匹配的时候才将消息传送给queue),那么topic这个规则就是模糊匹配,可以通过通配符满足一部分规则就可以传送,它的约定是:
1)routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”。
2)binding key与routing key一样也是句点号“. ”分隔的字符串。
3)binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)。
当生产者发送消息routing key=f.c.e的时候,这时候只满足queue1,所以会被路由到queue1中;如果routing key=a.c.e这时候会被同时路由到queue1和queue2中;如果routing key=a.f.b时,这时只会路由到queue2中。
- headers
headers类型的exchange,不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。
在绑定exchange与queue时指定一组键值对,当消息发送到exchange时,rabbitmq会取到该消息的headers(也是一个键值对的形式),并对比其中的键值对是否完全匹配exchange与queue绑定时指定的键值对,如果完全匹配则消息会路由到该queue,否则不会路由到该queue。该类型的exchange没有用到过(不过也应该很有用武之地),所以不作介绍。
下面对4种exchange类型进行简要的表格整理:
类型名称 | 类型描述 |
fanout | 把所有发送到该exchange的消息路由到所有与它绑定的queue中 |
direct | routing key==binding key |
topic | 简称模糊匹配 |
headers | exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。 |
2.4、补充说明
connectionfactory、connection、channel:都是rabbitmq对外api中最基本的对象。
connectionfactory:connection的制造工厂。
connection:rabbitmq的socket链接,它封装了socket协议相关的逻辑。connection建立一个tcp连接,生产者和消费者通过tcp连接到rabbitmq server。
channel:
1)channel是与rabbitmq打交道的最重要的一个接口,我们大部分的业务操作都是通过channel这个接口来完成的,包括定义exchange与queue、绑定exchange与queue、发布消息等。
2)channel虚拟连接,是建立在上面tcp连接的基础上。数据流动都是通过channel来进行的,为什么不是直接在tcp连接上进行数据流动呢?是因为建立和关闭tcp连接是有代价的。频繁地建立与关闭tcp连接对于系统的性能有很大的影响,而且tcp的连接数也有限制,这也限制了系统处理高并发的能力,而在tcp连接中建立channel是没有上述代价的。
参考自:
https://www.cnblogs.com/dwlsxj/p/rabbitmq.html