1.消息队列的优点
解耦 异步 削峰
2.Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
blog.csdn.net/Dome_/artic…
3.rabbitmq中的五种消费模式
简单队列和work模式
三种交换机模式
解决了我的一个疑惑,多个客户端订阅同一个队列,消息会分摊,不是每个客户端都得到所有的消息,同一个消息只会被一个客户端消费
生产者将消息给交换器,交换器根据路由键将消息路由到一个或者多个队列中
6.RoutingKey:路由键。生产者将消息发给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则,而这个RoutingKey需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键(BindingKey)固定的情况下,生产者可以在发送消息给交换器时,通过指定RoutingKey来决定消息流向哪里。
Binding:绑定。RabbitMQ中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键( BindingKey ) ,这样RabbitMQ 就知道如何正确地将消息路由到队列了, 如图
生产者将消息发送给交换器时, 需要一个RoutingKey , 当BindingKey 和RoutingKey 相匹配时,消息会被路由到对应的队列中。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的BindingKey.BindingKey并不是在所有的情况下都生效,它依赖于交换器类型,比如fanout类型的交换器就会无视BindingKey,而是将消息路由到所有绑定到该交换器的队列中。
7.文字描述生产者生产消息,消费者消费消息过程
初状态下,生产者发送消息的时候
(1 )生产者连接到RabbitMQ Broker , 建立一个连接( Connection ),开启一个信道( Channel )
(2 )生产者声明一个交换器,并设置相关属性,比如交换机类型、是否持久化等
(3 )生产者声明一个队列井设置相关属性,比如是否排他、是否持久化、是否自动删除等
( 4 )生产者通过路由键将交换器和队列绑定起来
( 5 )生产者发送消息至RabbitMQBroker,其中包含路由键、交换器等信息
(6 )相应的交换器根据接收到的路由键查找相匹配的队列
( 7 )如果找到,则将从生产者发送过来的消息存入相应的队列中
(8 )如果没有找到,则根据生产者配置的属性选择丢弃还是回退给生产者
(9 )关闭信道
(10 )关闭连接
消费者接收消息的过程:
( 1) 消费者连接到RabbitMQ Broker ,建立一个连接( Connection ),开启一个信道( Channel)
(2 )消费者向RabbitMQBroker请求消费相应队列中的消息,可能会设置相应的回调函数,
以及做一些准备工作
(3 )等待RabbitMQ Broker 回应并投递相应队列中的消息,消费者接收消息
(4 )消费者确认( ack )接收到的消息
(5 ) RabbitMQ 从队列中删除相应己经被确认的消息
(6 )关闭信道
(7 )关闭连接
8.为何使用信道channel,以及信道的瓶颈
Connection 可以用来创建多个Channel 实例,但是Channel 实例不能在线程问共享
如果在使用Channel 的时候其己经处于关闭状态,那么程序会抛出一个异常,我们只需捕获这个异常即可
显式地关闭Channel 是个好习惯,但这不是必须的,在Co口口ection 关闭的时候,Channel 也会自动关闭
9.AMQP协议理解
AMQP 本身是应用层的协议,其填充于TCP 协议层的数据部分
RabbitMQ 可以看作AMQP 协议的具体实现
10.生产者和消费者都可以声明一个交换器或者队列。如果尝试声明一个已经存在的交换器或者队列,只要声明的参数完全匹配现存的交换器或者队列,RabbitMQ就可以什么都不做, 并成功返回。如果声明的参数不匹配则会抛出异常
(怪不得消费者每次也会declare一下,以防万一)
11.autoack的作用
12.函数参数理解
发布消息时,当mandatory参数设为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitMQ会调用Basic.Return命令将消息返回给生产者,生产者可以通过调用channel.addReturnListener来添加ReturnListener监昕器实现获取到没有被正确路由到合适队列的消息。当mandatory参数设置为false时,出现上述情形,则消息直接被丢弃。
当immediate 参数设为true 时,如果交换器在将消息路由到队列时发现队列上并不存在
任何消费者,那么这条消息将不会存入队列中。当与路由键匹配的所有队列都没有消费者时,该消息会通过Basic .Return 返回至生产者。
概括来说, mandatory参数设为true时参数告诉服务器至少将该消息路由到一个队列中,否则将消息返回给生产者。immediate参数告诉服务器,如果该消息关联的队列上有消费者,则立刻投递如果所有匹配的队列上都没有消费者,则直接将消息返还给生产者,不用将消息存入队列等待消费者了。
13.如何确保发送方消息到达服务器?
事务机制在一条消息发送之后会使发送端阻塞,以等待RabbitMQ 的回应,之后才能继续
发送下一条消息。相比之下,发送方确认机制最大的好处在于它是异步的,一旦发布一条消息,
生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之
后,生产者应用程序便可以通过回调方法来处理该确认消息,如果RabbitMQ 因为自身内部错
误导致消息丢失,就会发送一条nack (Basic . Nack )命令,生产者应用程序同样可以在回调方法中处理该nack 命令。
事务和publisher confirm 机制确保的是消息能够正确地发送至RabbitMQ ,这里的
发送至RabbitMQ”的含义是指消息被正确地发往至RabbitMQ 的交换器,如果此交换器没有
匹配的队列,那么消息也会丢失。所以在使用这两种机制的时候要确保所涉及的交换器能够有
匹配的队列. 更进一步地讲,发送方要配合mandatory 参数或者备份交换器一起使用来提高
消息传输的可靠性。
14. 关于消费端消息的几点结论
消息分发
当RabbitMQ 队列拥有多个消费者时,队列收到的消息将以轮询( round-robin )的分发方式
发送给消费者。每条消息只会发送给订阅列表里的一个消费者。这种方式非常适合扩展,而且
它是专门为并发程序设计的。如果现在负载加重,那么只需要创建更多的消费者来消费处理消
息即可。
很多时候轮询的分发机制也不是那么优雅。默认情况下,如果有n 个消费者,那么RabbitMQ
会将第m 条消息分发给第m%n (取余的方式)个消费者, RabbitMQ 不管消费者是否消费并己
经确认( Basic.Ack )了消息。试想一下,如果某些消费者任务繁重,来不及消费那么多的消
息,而某些其他消费者由于某些原因(比如业务逻辑简单、机器性能卓越等)很快地处理完了
所分配到的消息,进而进程空闲,这样就会造成整体应用吞吐量的下降。
那么该如何处理这种情况呢?这里就要用到channel.basicQos(int prefetchCount)
这个方法,如前面章节所述, channel.basicQos 方法允许限制信道上的消费者所能保持的最大
未确认消息的数量。
举例说明,在订阅消费队列之前,消费端程序调用了channel.basicQos(S ),之后订
阅了某个队列进行消费。RabbitMQ 会保存一个消费者的列表,每发送一条消息都会为对应的消
费者计数,如果达到了所设定的上限,那么RabbitMQ 就不会向这个消费者再发送任何消息。
直到消费者确认了某条消息之后, RabbitMQ 将相应的计数减1 ,之后消费者可以继续接收消息,
直到再次到达计数上限。这种机制可以类比于TCP/IP 中的“滑动窗口”
消息顺序
消息的顺序性是指消费者消费到的消息和发送者发布的消息的顺序是一致的。举个例子,
不考虑消息重复的情况,如果生产者发布的消息分别为msgl 、msg2 、msg3 ,那么消费者必然
也是按照msgl 、msg2 、msg3 的顺序进行消费的。
目前很多资料显示RabbitMQ 的消息能够保障顺序性,这是不正确的,或者说这个观点有
很大的局限性。在不使用任何RabbitMQ 的高级特性,也没有消息丢失、网络故障之类异常的
情况发生,并且只有一个消费者的情况下,最好也只有一个生产者的情况下可以保证消息的顺
序性。如果有多个生产者同时发送消息,无法确定消息到达Broker 的前后顺序,也就无法验证
消息的顺序性。
内存溢出
消息保障传输
消息可靠传输一般是业务系统接入消息中间件时首要考虑的问题, 一般消息中间件的消息
传输保障分为三个层级。
- At most once :最多一次。消息可能会丢失,但绝不会重复传输。
- At least once :最少一次。消息绝不会丢失,但可能会重复传输。
- Exactly once :恰好一次。每条消息肯定会被传输一次且仅传输一次。
RabbitMQ 支持其中的“最多一次”和“最少一次”。其中“最少一次”投递实现需要考虑
以下这个几个方面的内容:
(1) 消息生产者需要开启事务机制或者publisher confirm 机制,以确保消息可以可靠地传 输到RabbitMQ 中。
(2 )消息生产者需要配合使用mandatory 参数或者备份交换器来确保消息能够从交换器 路由到队列中,进而能够保存下来而不会被丢弃。
(3 )消息和队列都需要进行持久化处理,以确保RabbitMQ 服务器在遇到异常情况时不会 造成消息丢失。
(4 )消费者在消费消息的同时需要将autoAck 设置为false,然后通过手动确认的方式去 确认己经正确消费的消息,以避免在消费端引起不必要的消息丢失。
“最多一次”的方式就无须考虑以上那些方面,生产者随意发送,消费者随意消费,不过这 样很难确保消息不会丢失。
“恰好一次”是RabbitMQ 目前无法保障的(想要解决消息的重复问题,只能消费者自己去编码保证消息的最终一致,自己实现幂等功能)
15.总结:论如何确保一个消息从生产到消费
事务机制或者publisher confirm可以确保消息到达了服务器
mandatory参数确保有队列接收消息(否则返回给生产者)
消息,队列都持久化确保服务器异常数据不会丢失
确认机制ack在消费者收到消息之后才删除消息
16.集群中数据的存储
RabbitMQ 集群对延迟非常敏感,应当只在本地局域网内使用。在广域网中不应该使 用集群,而应该使用Federation 或者Shovel 来代替。
16.镜像队列
如果RabbitMQ 集群是由多个Broker 节点组成的,那么从服务的整体可用性上来讲,该集
群对于单点故障是有弹性的,但是同时也需要注意:尽管交换器和绑定关系能够在单点故障问
题上幸免于难,但是队列和其上的存储的消息却不行,这是因为队列进程及其内容仅仅维持在
单个节点之上,所以一个节点的失效表现为其对应的队列不可用。引入镜像队列( Mirror Queue )的机制,可以将队列镜像到集群中的其他Broker 节点之上,
如果集群中的一个节点失效了,队列能自动地切换到镜像中的另一个节点上以保证服务的可用
性。在通常的用法中,针对每一个配置镜像的队列(以下简称镜像队列〉都包含一个主节点
(master )和若干个从节点( slave
slave 会准确地按照master 执行命令的顺序进行动作,故slave 与master 上维护的状态应该
是相同的。如果master 由于某种原因失效,那么“资历最老”的slave 会被提升为新的master 。
根据slave 加入的时间排序,时间最长的slave 即为“资历最老”。发送到镜像队列的所有消息会
被同时发往master 和所有的slave 上,如果此时master 挂掉了,消息还会在slave 上,这样slave
提升为master 的时候消息也不会丢失如果消费者与slave 建立连接井进行订阅消费,其实质上都是从master 上获取消息,只不
过看似是从slave 上消费而己。比如消费者与slave 建立了TCP 连接之后执行一个Basic.Get
的操作,那么首先是由slave 将Basic.Get 请求发往master ,再由master 准备好数据返回给
slave ,最后由slave 投递给消费者。读者可能会有疑问,大多的读写压力都落到了master 上,
那么这样是否负载会做不到有效的均衡?或者说是否可以像MySQL 一样能够实现master 写而
slave 读呢?注意这里的master 和slave 是针对队列而言的,而队列可以均匀地散落在集群的各
个Broker 节点中以达到负载均衡的目的,因为真正的负载还是针对实际的物理机器而言的,而
不是内存中驻留的队列进程。
17. rabbitmq 持久化有什么缺点?
持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。
18.rabbitmq 怎么实现延迟消息队列?
给一个消息队列设置一个ttl,再配置一个死信队列,没有消费者去消费这个队列,等时间过了ttl之后,消息会进入到死信队列,然后消费者去消费死信队列,从而达到延迟消费的功能;
使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。
19.rabbitmq 集群有什么用?
集群主要有以下两个用途:
高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
高容量:集群可以承载更多的消息量