RabbitMQ 面试题

192 阅读8分钟

解耦、异步、削峰是什么?

  • 解耦:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃…A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

    就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。

  • 异步:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。

  • 削峰:减少高峰时期对服务器压力。

以上内容取自以下链接:

RabbitMQ面试题(总结最全面的面试题)

RabbitMQ基本概念

RabbitMQ的5大核心概念:Connection(连接)、Channel(信道)、Exchange(交换机)、Queue(队列)、Virtual host(虚拟主机)。

在介绍这些概念之前,我们先看一张图,图中展示的是RabbitMQ的工作模型,根据这张图,下面理解起来就比较容易了:

0dd7912397dda1443fe8006d997086a80ef48659.jpeg

image.png

其中,中间的Broker表示RabbitMQ服务,每个Broker里面至少有一个Virtual host虚拟主机,每个虚拟主机中有自己的Exchange交换机、Queue队列以及Exchange交换机与Queue队列之间的绑定关系Binding。producer(生产者)和consumer(消费者)通过与Broker建立Connection来保持连接,然后在Connection的基础上建立若干Channel信道,用来发送与接收消息。

Connection(连接)

每个producer(生产者)或者consumer(消费者)要通过RabbitMQ发送与消费消息,首先就要与RabbitMQ建立连接,这个连接就是Connection。Connection是一个TCP长连接。

Channel(信道)

Channel是在Connection的基础上建立的虚拟连接,RabbitMQ中大部分的操作都是使用Channel完成的,比如:声明Queue、声明Exchange、发布消息、消费消息等。

看到此处,你是否有这样一个疑问:既然已经有了Connection,我们完全可以使用Connection完成Channel的工作,为什么还要引入Channel这样一个虚拟连接的概念呢?因为现在的程序都是支持多线程的,如果没有Channel,那么每个线程在访问RabbitMQ时都要建立一个Connection这样的TCP连接,对于操作系统来说,建立和销毁TCP连接是非常大的开销,在系统访问流量高峰时,会严重影响系统性能。

Channel就是为了解决这种问题,通常情况下,每个线程创建单独的Channel进行通讯,每个Channel都有自己的channel id帮助Broker和客户端识别Channel,所以Channel之间是完全隔离的。

Connection与Channel之间的关系可以比作光纤电缆,如果把Connection比作一条光纤电缆,那么Channel就相当于是电缆中的一束光纤。

Virtual host(虚拟主机)

Virtual host是一个虚拟主机的概念,一个Broker中可以有多个Virtual host,每个Virtual host都有一套自己的Exchange和Queue,同一个Virtual host中的Exchange和Queue不能重名,不同的Virtual host中的Exchange和Queue名字可以一样。这样,不同的用户在访问同一个RabbitMQ Broker时,可以创建自己单独的Virtual host,然后在自己的Virtual host中创建Exchange和Queue,很好地做到了不同用户之间相互隔离的效果。

0dd7912397dda1443fe8006d997086a80ef48659.jpeg

Queue(队列)

Queue是一个用来存放消息的队列,生产者发送的消息会被放到Queue中,消费者消费消息时也是从Queue中取走消息。

Exchange(交换机)

Exchange是一个比较重要的概念,它是消息到达RabbitMQ的第一站,主要负责根据不同的分发规则将消息分发到不同的Queue,供订阅了相关Queue的消费者消费到指定的消息。那Exchange有哪些分发消息的规则呢?这就要说到Exchange的4种类型了:direct、fanout、topic、headers。

我们了解一下另外一个比较重要的概念:Routing key,翻译成中文就是路由键。当我们创建好Exchange和Queue之后,需要使用Routing key(通常叫作Binding key)将它们绑定起来,producer在向Exchange发送一条消息的时候,必须指定一个Routing key,然后Exchange接收到这条消息之后,会解析Routing key,然后根据Exchange和Queue的绑定规则,将消息分发到符合规则的Queue中。

0dd7912397dda1443fe8006d997086a80ef48659.jpeg

以上内容取自以下链接:

RabbitMQ的工作模式及原理

RabbitMQ的工作模式

  • 简单队列模式

不用显示声明交换机,只需声明一个队列,生产者指定队列名发送消息给MQ,然后会有一个默认的交换机将消息转发给这个队列。消费者负责监听这个队列,一有消息就会得到通知做出响应。

image.png

  • 工作队列模式(Work queues)

和简单队列模式基本一样,不过有一点不同,该模式有多个消费者在监听队列。RabbitMQ会以轮询的方式将消息发给多个消费者确保一条消息只会被一个消费者消费。

image.png

  • 发布订阅模式(Publish/subscribe)

和上面2种模式默认提供交换机不同的是,该模式需要显示声明交换机,然后可以创建多个队列和这个交换机进行绑定。生产者发消息给mq时需要指定交换机,然后交换机将消息转发给与自己绑定的所有队列。消费者监听指定的队列获得消息。每个队列可以有多个消费者监听,同样也是以轮询的机制发给消费者。

生活中的案例:就是玩抖音快手,众多粉丝关注一个视频主,视频主发布视频,所有粉丝都可以得到视频通知。

image.png

上图中,X就是视频主,红色的队列就是粉丝。binding是绑定的意思(关注),P生产者发送信息给X路由,X将信息转发给绑定X的队列。

image.png

X队列将信息通过信道发送给消费者,从而进行消费。

整个过程,必须先创建路由,路由在生产者程序中创建,因为路由没有存储消息的能力,当生产者将信息发送给路由后,消费者还没有运行,所以没有队列,路由并不知道将信息发送给谁。

运行程序的顺序:

1、先运行生产者sender;创建路由;

2、在运行消费者Recer1、Recer2。

  • Routing 模式

和发布订阅模式不同的是,队列绑定交换机时需要指定一个routingkey,那么生产者发送消息时不仅需要指定交换机还需要指定routingkey,这样的话交换机就会把消息转发给跟自己绑定并且routingkey相匹配的队列。

image.png

  • Topic模式

和Routing模式唯一的不同就是可以设置带有通配符进行模糊匹配的routingkey。

image.png

匹配符号:

*:只能匹配一个词(正好一个词,多一个不行,少一个也不行)

#:匹配0个或更多个词

官网案例:

Q1绑定了路由键  *.orange.*  Q2绑定了路由键  *.*.rabbitlazy.#,下面生产者的消息会被发送给哪个队列?

quick.orange.rabbit 	        # Q1 Q2
lazy.orange.elephant 	        # Q1 Q2
quick.orange.fox 		# Q1
lazy.brown.fox 			# Q2
lazy.pink.rabbit 		# Q2
quick.brown.fox			# 无
orange 				# 无
quick.orange.male.rabbit        # 无
  • Headers模式

和Routing模式的不同就是取消了routing 使用键值对的方式作为routingkey。

几种常见路由件说明:

  • 发布/订阅模式
// fanout:不处理路由键(只需要将队列绑定到路由上,
// 发送到路由的消息都会被转发到与该路由绑定的所有队列上)
channel.exchangeDeclare(“test_exchange_fanout”,“fanout”);
  • 路由模式
//direct:会根据路由键进行定向分发消息
channel.exchangeDeclare(“router_exchange_direct”,“direct”);
  • 主题模式
// topic:模糊匹配的定向分发
channel.exchangeDeclare(“test_exchange_topic”, “topic”);

以上内容取自以下链接:

RabbitMQ的几种工作模式(有详细代码实现)