消息队列(RabbitMQ&&Kafka)

by《RabbitMQ实战指南》

消息队列MQ

消息队列,知识点概述】消息队列基本介绍。消息队列的特性。消息队列的作用。主流的消息中间件及其对比。使用消息队列带来的一些问题。消息队列MQ两种传递模式。

消息队列MQ,基本介绍】
消息队列中间件,也可以称为消息队列或者消息中间件。
消息队列中间件(Message Queue Middleware,简称为 MQ)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。

消息中间件,作用】
消息中间件提供了有保证的消息发送,应用程序开发人员无须了解远程过程调用(RPC)和网络通信协议的细节。消息中间件适用于需要可靠的数据传送的分布式环境。采用消息中间件的系统中,不同的对象之间通过传递消息来激活对方的事件,以完成相应的操作。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接收者。消息中间件能在不同平台之间通信,它常被用来屏蔽各种平台及协议之间的特性,实现应用程序之间的协同,其优点在于能够在客户和服务器之间提供同步和异步的连接,并且在任何时刻都可以将消息进行传送或者存储转发,这也是它比远程过程调用更进步的原因。举例说明,应用程序 A 与应用程序 B 通过使用消息中间件的应用程序编程接口发送消息来进行通信。消息中间件将消息路由给应用程序 B,这样消息就可存在于完全不同的计算机上。消息中间件负责处理网络通信,如果网络连接不可用,消息中间件会存储消息,直到连接变得可用,再将消息转发给应用程序 B。灵活性的另一方面体现在,当应用程序 A 发送其消息时,应用程序 B 甚至可以处于不运行状态,消息中间件将保留这份消息,直到应用程序 B 开始执行并消费消息,这样还防止了应用程序 A 因为等待应用程序 B 消费消息而出现阻塞。这种异步通信方式要求应用程序的设计与现在大多数应用不同。不过对于时间无关或并行处理的场景,它可能是一个极其有用的方法。

消息中间件,特性】

解耦:在项目启动之初来预测将来会碰到什么需求是极其困难的。消息中间件在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,这允许你独立地扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束即可

冗余(存储):有些情况下,处理数据的过程会失败。消息中间件可以把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。在把一个消息从消息中间件中删除之前,需要你的处理系统明确地指出该消息已经被处理完成,从而确保你的数据被安全地保存直到你使用完毕。

扩展性:因为消息中间件解耦了应用的处理过程,所以提高消息入队和处理的效率是很容易的,只要另外增加处理过程即可,不需要改变代码,也不需要调节参数。

削峰:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果以能处理这类峰值为标准而投入资源,无疑是巨大的浪费。使用消息中间件能够使关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。

可恢复性:当系统一部分组件失效时,不会影响到整个系统。消息中间件降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入消息中间件中的消息仍然可以在系统恢复后进行处理。

顺序保证:在大多数使用场景下,数据处理的顺序很重要,大部分消息中间件支持一定程度上的顺序性。

缓冲:在任何重要的系统中,都会存在需要不同处理时间的元素。消息中间件通过一个缓冲层来帮助任务最高效率地执行,写入消息中间件的处理会尽可能快速。该缓冲层有助于控制和优化数据流经过系统的速度。

异步通信:在很多时候应用不想也不需要立即处理消息。消息中间件提供了异步处理机制,允许应用把一些消息放入消息中间件中,但并不立即处理它,在之后需要的时候再慢慢处理。

消息队列的作用】
资料)1.流量消峰
举个例子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单一秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这时有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。
mine----先将短时间高并发产生的事务消息存储在消息队列中,然后后端服务再慢慢根据自己的能力去消费这些消息,这样就避免直接把后端服务打垮掉。

2.应用解耦
以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障,都会造成下单操作异常。当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障,提升系统的可用性。

3.异步处理---------------mine:不太理解。
有些服务间调用是异步的,例如 A 调用 B,B 需要花费很长时间执行,但是 A 需要知道 B 什么时候可以执行完,以前一般有两种方式,A 过一段时间去调用 B 的查询 api 查询。或者 A 提供一个 callback api, B 执行完之后调用 api 通知 A 服务。这两种方式都不是很优雅,使用消息总线,可以很方便解决这个问题,A 调用 B 服务后,只需要监听 B 处理完成的消息,当 B 处理完成后,会发送一条消息给 MQ,MQ 会将此消
息转发给 A 服务。这样 A 服务既不用循环调用 B 的查询 api,也不用提供 callback api。同样B 服务也不用做这些操作。A 服务还能及时的得到异步处理成功的消息。

使用消息队列带来的一些问题】
系统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入 MQ 之前,你不用考虑消息丢失或者说 MQ 挂掉等等的情况,但是,引入 MQ 之后你就需要去考虑了!
系统复杂性提高: 加入 MQ 之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
一致性问题: 消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!

主流的消息中间件】RabbitMQ、Kafka、ActiveMQ、RocketMQ。

MQ 的选择】
资料)1.Kafka
Kafka 主要特点是基于Pull 的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。大型公司建议可以选用,如果有日志采集功能,肯定是首选 kafka 了。
Kafka 的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时 kafka 最好是支撑较少的 topic 数量即可,保证其超高吞吐量。kafka 唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集

尚硅谷官网 kafka 视频连http://www.gulixueyuan.com/course/330/tasks
2.RocketMQ。天生为金融互联网领域而生,对于可靠性要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰,在大量交易涌入时,后端可能无法及时处理的情况。RoketMQ 在稳定性上可能更值得信赖,这些业务
场景在阿里双 11 已经经历了多次考验,如果你的业务有上述并发场景,建议可以选择 RocketMQ。
RocketMQ 阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的 MQ,并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。

3.RabbitMQ。 结合 erlang 语言本身的并发优势,性能好时效性微秒级,社区活跃度也比较高,管理界面用起来十分方便,如果你的数据量没有那么大,中小型公司优先选择功能比较完备的 RabbitMQ。
在这里插入图片描述

消息队列MQ两种传递模式】:点对点(P2P,Point-to-Point)模式和发布/订阅(Pub/Sub)模式。
点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息,队列的存在使得消息的异步传输成为可能。mine–一个消息只有一个消费者

发布订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题(topic),主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,而消息订阅者则从主题中订阅消息。主题使得消息的订阅者与消息的发布者互相保持独立,不需要进行接触即可保证消息的传递,发布/订阅模式在消息的一对多广播时采用。

RabbitMQ

RabbitMQ,知识点概述】RabbitMQ 基本介绍。"消息"是什么。RabbitMQ特点。RabbitMQ组成部分及其介绍。RabbitMQ的工作过程。AMQP协议。如何使用RabbitMQ进行客户端开发。 RabbitMQ,相关工具的使用。RabbitMQ配置。RabbitMQ,分布式部署(集群、Federation 和 Shovel。)RabbitMQ实现原理。使用RabbitMQ时的网络分区问题。RabbitMQ的持久化。RabbitMQ,考虑消息丢失情况和解决措施。“生产者发出后,消息有没有正确到达?”消息中间件的可靠性传输。消息的顺序性。

RabbitMQ 基本介绍

RabbitMQ 基本介绍】RabbitMQ 是目前非常热门的一款消息中间件,不管是互联网行业还是传统行业都在大量地使用。RabbitMQ 凭借其高可靠、易扩展、高可用及丰富的功能特性受到越来越多企业的青睐。RabbitMQ 是采用 Erlang 语言实现 AMQP(Advanced Message Queuing Protocol,高级消息队列协议)的消息中间件。RabbitMQ 客户端可以支持很多种语言。RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。从计算机术语层面来说,RabbitMQ 模型更像是一种交换机模型。自查RabbitMQ 的模型架构。

"消息"是什么

"消息"是什么】消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串、JSON 等,也可以很复杂,比如内嵌对象。消息一般可以包含 2 个部分:消息体和标签(Label)。消息体也可以称之为 payload,在实际应用中,消息体一般是一个带有业务逻辑结构的数据,比如一个 JSON 字符串。当然可以进一步对这个消息体进行序列化操作。消息的标签用来表述这条消息,比如一个交换器的名称和一个路由键。生产者把消息交由 RabbitMQ,
RabbitMQ 之后会根据标签把消息发送给感兴趣的消费者(Consumer)。

消息一般由 2 部分组成:消息头(或者说是标签 Label)和 消息体。消息体也可以称为 payLoad ,消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括 routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。

RabbitMQ,特点

RabbitMQ,特点】
可靠性:RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认及发布确认等。

灵活的路由:在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能,RabbitMQ 已经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个交换器绑定在一起,也可以通过插件机制来实现自己的交换器。

扩展性:多个 RabbitMQ 节点可以组成一个集群,也可以根据实际业务情况动态地扩展
集群中节点。

高可用性:队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队列仍然可用。

多种协议:RabbitMQ 除了原生支持 AMQP 协议,还支持 STOMP、MQTT 等多种消息中间件协议。

多语言客户端:RabbitMQ 几乎支持所有常用语言,比如 Java、Python、Ruby、PHP、C#、JavaScript 等。

管理界面:RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集群中的节点等。

插件机制:RabbitMQ 提供了许多插件,以实现从多方面进行扩展,当然也可以编写自己的插件。

RabbitMQ组成部分及其介绍

在这里插入图片描述

RabbitMQ中的生产者Producer】Producer:生产者,就是投递消息的一方,生产者创建消息,然后发布到 RabbitMQ 中。

RabbitMQ的消费者Consumer】Consumer:消费者,就是接收消息的一方。消费者连接到 RabbitMQ 服务器,并订阅到队列上。当消费者消费一条消息时,只是消费消息的消息体(payload)。在消息路由的过程中,消息的标签会丢弃,存入到队列中的消息只有消息体,消费者也只会消费到消息体,也就不知道消息的生产者是谁,当然消费者也不需要知道。

RabbitMQ Broker】Broker:消息中间件的服务节点。一个 RabbitMQ Broker 可以简单地看作一个 RabbitMQ 服务节点,或者 RabbitMQ 服务实例。大多数情况下也可以将一个 RabbitMQ Broker 看作一台 RabbitMQ服务器。

交换器Exchange】
"生产者将消息投递到队列中"这个过程实际上在RabbitMQ中不会发生,真实情况是,生产者将消息发送到 Exchange(交换器,通常也可以用大写的“X”来表示),由交换器将消息路由到一个或者多个队列中。如果路由不到,或许会返回给生产者,或许直接丢弃。这里可以将 RabbitMQ 中的交换器看作一个简单的实体。

队列Queue】队列,是 RabbitMQ 的内部对象,用于存储消息。RabbitMQ 中消息都只能存储在队列中。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。RabbitMQ 不支持队列层面的广播消费,如果需要广播消费,需要在其上进行二次开发,处理逻辑会变得异常复杂,同时也不建议这么做。

RabbitMQ 交换器Exchange详解、路由键、绑定

交换器Exchange】
"生产者将消息投递到队列中"这个过程实际上在RabbitMQ中不会发生,真实情况是,生产者将消息发送到 Exchange(交换器,通常也可以用大写的“X”来表示),由交换器将消息路由到一个或者多个队列中。如果路由不到,或许会返回给生产者,或许直接丢弃。这里可以将 RabbitMQ 中的交换器看作一个简单的实体。

RabbitMQ 中的交换器有四种类型】
RabbitMQ 常用的交换器类型有 fanout、direct、topic、headers 这四种。
不同的类型有着不同的路由策略

RoutingKey:路由键】生产者将消息发给交换器的时候,一般会指定一个 RoutingKey,用来指定这个消息的路由规则,而这个 Routing Key 需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键(BindingKey)固定的情况下,生产者可以在发送消息给交换器时,通过指定 RoutingKey 来决定消息流向哪里。

Binding:绑定】RabbitMQ 中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键(BindingKey),这样 RabbitMQ 就知道如何正确地将消息路由到队列了。

理解交换器、路由键、绑定这几个概念】
交换器相当于投递包裹的邮箱,RoutingKey 相当于填写在包裹上的
地址,BindingKey 相当于包裹的目的地,当填写在包裹上的地址和实际想要投递的地址相匹配时,那么这个包裹就会被正确投递到目的地,最后这个目的地的“主人”——队列可以保留这个包裹。如果填写的地址出错,邮递员不能正确投递到目的地,包裹可能会回退给寄件人,也有可能被丢弃。

注:大多数情况下习惯性地将 BindingKey写成 RoutingKey,尤其是在使用 direct 类型的交换器的时候。资料中多把两者合称为路由键,需要注意区分其中的不同,可以根据"送信过程"辨别方法进行有效的区分。

RabbitMQ队列Queue详解、 连接Connection、信道Channel

队列Queue】队列,是 RabbitMQ 的内部对象,用于存储消息。RabbitMQ 中消息都只能存储在队列中。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。RabbitMQ 不支持队列层面的广播消费,如果需要广播消费,需要在其上进行二次开发,处理逻辑会变得异常复杂,同时也不建议这么做。

连接Connection】无论是生产者还是消费者,都需要和 RabbitMQ Broker 建立连接,这个连接就是一条 TCP 连接,也就是Connection。

信道Channel】
一旦 TCP 连接建立起来,客户端紧接着可以创建一个 AMQP 信道(Channel),每个信道都会被指派一个唯一的 ID。信道是建立在Connection 之上的虚拟连接。RabbitMQ 处理的每条 AMQP 指令都是通过信道完成的。引入信道的原因–见书p25.

RabbitMQ的工作过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AMQP协议

AMQP协议】(Advanced Message Queuing Protocol,高级消息队列协议)。它是应用层协议的一个开放标准,以解决众多消息中间件的需求和拓扑结构问题。它为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。RabbitMQ是遵从AMQP协议的,换句话说,RabbitMQ就是AMQP协议的 Erlang 的实现。AMQP 的模型架构:生产者将消息发送给交换器,交换器和队列绑定,当生产者发送消息时所携带的 RoutingKey 与绑定时的 BindingKey 相匹配时,消息即被存入相应的队列之中。消费者可以订阅相应的队列来获取消息-----和 RabbitMQ 的模型架构是一样的。AMQP 说到底还是一个通信协议,通信协议都会涉及报文交互,从 low-level 举例来说,AMQP 本身是应用层的协议,其填充于 TCP 协议层的数据部分。而从 high-level 来说,AMQP是通过协议命令进行交互的。AMQP 协议可以看作一系列结构化命令的集合,这里的命令代表一种操作,类似于 HTTP 中的方法(GET、POST、PUT、DELETE 等)。

AMQP 协议本身包括三层:Module Layer、Session Layer、 Transport Layer。

AMQP 协议命令的流转过程】通过生产者和消费者流转过程分析。—结合代码分析—见书p27。

AMQP 命令】AMQP 命令学习切入点:包含名称、是否包含内容体(Content Body)、对应客户端中相应的方法及简要描述。

如何使用RabbitMQ进行客户端开发

RabbitMQ Java客户端使用com.rabbitmq.client作为顶级包名,关键的Class和Interface有 Channel、Connection、ConnectionFactory、Consumer 等。AMQP 协议层面的操作通过 Channel 接口实现。Connection 是用来开启 Channel(信道)的,可以注册事件处理器,也可以在应用结束时关闭连接。与 RabbitMQ 相关的开发工作,基本上也是围绕 Connection和 Channel 这两个类展开的。

生产者客户端的代码首先和 RabbitMQ 服务器建立一个连接(Connection),然后在这个连接之上创建一个信道(Channel)。之后创建一个交换器(Exchange)和一个队列(Queue),并通过路由键进行绑定。然后发送一条消息,最后关闭资源。使用RabbitMQ进行客户端开发,知识点大致为这几个部分:连接 RabbitMQ、使用交换器和队列、发送消息、消费消息、消费端的确认与拒绝、关闭连接。每一个部分下又各自有更详细的内容和代码实现,比如具体的类和方法的使用。

exchangeDeclare 方法详解、queueDeclare 方法详解、queueBind 方法详解、exchangeBind 方法详解。

RabbitMQ,相关工具的使用

RabbitMQ 的一些工具应用,包括 rabbitmqctl工具和 rabbitmq_management 插件。rabbitmqctl 工具是一个系列的工具,运用这个工具可以执行大部分的 RabbitMQ 的管理操作。而 rabbitmq_management 插件是 RabbitMQ 提
供的一个管理插件,让用户可以通过图形化的方式来管理 RabbitMQ

RabbitMQ,配置

一般情况下,可以使用默认的内建配置来有效地运行 RabbitMQ,并且大多数情况下也并不需要修改任何 RabbitMQ 的配置。当然,为了更加有效地操控 RabbitMQ,也可以利用调节系统范围内的参数来达到定制化的需求。
RabbitMQ 提供了三种方式来定制化服务:
(1)环境变量(Enviroment Variables)。RabbitMQ 服务端参数可以通过环境变量进行配置,
例如,节点名称、RabbitMQ 配置文件的地址、节点内部通信端口等。
(2)配置文件(Configuration File)。可以定义 RabbitMQ 服务和插件设置,例如,TCP 监听端口,以及其他网络相关的设置、内存限制、磁盘限制等。
(3)运行时参数和策略(Runtime Parameters and Policies)。可以在运行时定义集群层面的服务设置。对于不同的操作系统和不同的 RabbitMQ 安装包来说,相应的配置会有所变化,包括相应的配置文件的地址等,在使用时要尤为注意。

RabbitMQ 在配置这方面可谓相当完善,在很多情况下都可以使用默认的配置而不需要改变其中任何一个就可以让 RabbitMQ 很好地提供服务。不过也有一些特殊的情况,比如默认的 5672端口被其他的应用程序所占用,那么就需要修改环境变量 RABBITMQ_NODE_PORT 或者修改配置文件中的 tcp_listeners。如果需要尽可能地发挥 RabbitMQ 本身的性能,那么对于配置
参数的调优就显得至关重要了,比如禁用 Nagle 算法或者增大 TCP 缓冲区的大小可以提高吞吐量

RabbitMQ,分布式部署(集群、Federation 和 Shovel。)

RabbitMQ 可以通过 3 种方式实现分布式部署:集群、Federation 和 Shovel。这 3 种方式不是互斥的,可以根据需要选择其中的一种或者以几种方式的组合来达到分布式部署的目的。Federation 和 Shovel 可以为 RabbitMQ 的分布式部署提供更高的灵活性,但同时也提高了部署的复杂性。

RabbitMQ实现原理

了解一些 RabbitMQ 的实现原理也是很有必要的,它可以让你在遇到问题时能透过现象看本质。比如一个队列的内部存储其实是由 5 个子队列来流转运作的,队列中的消息可以有 4 种不同的状态等,通过这些可以明白在使用 RabbitMQ 时尽量不要有过多的消息堆积,不然会影响整体服务的性能。

“本章首先讲述了 RabbitMQ 的存储机制,进而对队列的结构展开讨论,队列中的消息有alpha、beta、gamma、delta 这 4 种状态,内部存储又可以分为 Q1、Q2、Delta、Q3、Q4这 5 个子队列。消息会在这 5 个子队列中流转,为了性能的提升需要尽可能地避免消息过量堆积。如果消息是持久化的,建立搭配惰性队列使用,这样在提升性能的同时还可以降低内存的
损耗。内存、磁盘和流控都是用来限制消息流入得过快以避免相应的服务进程来不及处理而崩溃。镜像队列的引入可以极大地提升 RabbitMQ 的可用性及可靠性,提供了数据冗余备份、避免单点故障的功能,强烈建议在实际应用中为每个重要的队列都配置镜像。”----------小结

使用RabbitMQ时的网络分区问题

“网络分区是在使用 RabbitMQ 时所不得不面对的一个问题,网络分区的发生可能会引起消息丢失或者服务不可用等。可以简单地通过重启的方式或者配置自动化处理的方式来处理这个问题,但深究其里会发现网络分区不是想象中的那么简单。通过网络分区的意义、影响、处理及案例分析等多个维度来一一剖析其中的奥秘”---------摘录书

“本章主要阐述了网络分区的意义,如何查看和处理网络分区及网络分区所带来的影响。虽然前面的内容没有提及,但是对于网络分区的监控也尤为重要。首先,若发生网络分区,客户端有可能会报错,相关的日志检测机制可以示警,但是此时无法确定发生了网络分区,需要进一步分析,从而影响处理网络分区的实效性。当然也可以通过人工的方式查看 Web 管理界面或
者使用 rabbitmqctl cluster_status 检测到网络分区的发生,但是这种非自动化的手段远非长久之计。真正可取的有两种方式:第一种是监测/api/nodes 这个 HTTP API 接口所返回的 JSON 字符串中 partitions 项中是否有节点信息;第二种是监测 RabbitMQ 的服务日志是否有 Mnesia(‘rabbit@node1’): ** ERROR ** mnesia_event got {inconsistent_ database, running_partitioned_network, ‘rabbit@node2’}类似的日志,可以参考
7.2 节。这两种方式都可以做成自动化的应用。”-----摘录书

RabbitMQ的持久化

RabbitMQ的持久化分为三个部分:交换器的持久化、队列的持久化和消息的持久化

交换器的持久化】
交换器的持久化是通过在声明队列是将 durable 参数置为 true 实现的,详细可以参考 3.2.1节。如果交换器不设置持久化,那么在 RabbitMQ 服务重启之后,相关的交换器元数据会丢失,不过消息不会丢失,只是不能将消息发送到这个交换器中了。对一个长期使用的交换器来说,建议将其置为持久化的。

队列的持久化】
队列的持久化是通过在声明队列时将 durable 参数置为 true 实现的,详细内容可以参考
3.2.2 节。如果队列不设置持久化,那么在 RabbitMQ 服务重启之后,相关队列的元数据会丢失,此时数据也会丢失。正所谓“皮之不存,毛将焉附”,队列都没有了,消息又能存在哪里呢?

消息的持久化】
队列的持久化能保证其本身的元数据不会因异常情况而丢失,但是并不能保证内部所存储的消息不会丢失。要确保消息不会丢失,需要将其设置为持久化。通过将消息的投递模式(BasicProperties 中的 deliveryMode 属性)设置为 2 即可实现消息的持久化。在使用 RabbitMQ 的时候,可以通过消息持久化操作来解决因为服务器的异常崩溃而导致的消息丢失。

持久化的设置】
单单只设置队列持久化,重启之后消息会丢失。
单单只设置消息的持久化,重启之后队列消失,继而消息也丢失。
单单设置消息持久化而不设置队列的持久化显得毫无意义。
设置了队列和消息的持久化,当 RabbitMQ 服务重启之后,消息依旧存在。
可以将所有的消息都设置为持久化,但是这样会严重影响 RabbitMQ 的性能(随机)。写入磁盘的速度比写入内存的速度慢得不只一点点。对于可靠性不是那么高的消息可以不采用持久化处理以提高整体的吞吐量。在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做一个权衡。

RabbitMQ,考虑消息丢失情况和解决措施

交换器、队列、消息都设置了持久化之后也不能百分之百保证数据不会丢失。

RabitMQ,数据丢失情况】
1.若订阅消费队列时将 autoAck 参数设置为 true,那么当消费者接收到相关消息之后,若还没来得及处理就宕机了,这样也算数据丢失。
解决措施:autoAck 参数设置为 false,并进行手动确认。

2.在持久化的消息正确存入 RabbitMQ 之后,还需要有一段时间(虽然很短,但是不可忽视)才能存入磁盘之中。RabbitMQ 并不会为每条消息都进行同步存盘(调用内核的 fsync1方法)的处理,可能仅仅保存到操作系统缓存之中而不是物理磁盘之中。如果在这段时间内RabbitMQ 服务节点发生了宕机、重启等异常情况,消息保存还没来得及落盘,那么这些消息将会丢失。
解决措施:
1.可以引入 RabbitMQ 的镜像队列机制(详细参考 9.4 节),相当
于配置了副本,如果主节点(master)在此特殊时间内挂掉,可以自动切换到从节点(slave),这样有效地保证了高可用性,除非整个集群都挂掉。虽然这样也不能完全保证 RabbitMQ 消息不丢失,但是配置了镜像队列要比没有配置镜像队列的可靠性要高很多,在实际生产环境中的关键业务队列一般都会设置镜像队列。
2.在发送端引入事务机制或者发送方确认机制来保证消息已经正确地发送并存储至RabbitMQ 中,前提还要保证在调用 channel.basicPublish 方法的时候交换器能够将消息正确路由到相应的队列之中。

“生产者发出后,消息有没有正确到达?”

当消息的生产者将消息发送出去之后,消息到底有没有正确地到达服务器呢?如果不进行特殊配置,默认情况下发送消息的操作是不会返回任何信息给生产者的,也就是默认情况下生产者是不知道消息有没有正确地到达服务器。如果在消息到达服务器之前已经丢失,持久化操作也解决不了这个问题,因为消息根本没有到达服务器,何谈持久化?
RabbitMQ 针对这个问题,提供了两种解决方式:
通过事务机制实现;
通过发送方确认(publisher confirm)机制实现。

消息中间件的可靠性传输

一般消息中间件的消息传输保障分为三个层级。
At most once:最多一次。消息可能会丢失,但绝不会重复传输。
At least once:最少一次。消息绝不会丢失,但可能会重复传输。
Exactly once:恰好一次。每条消息肯定会被传输一次且仅传输一次.
RabbitMQ 支持其中的“最多一次”和“最少一次”。

消息的顺序性

消息的顺序性是指消费者消费到的消息和发送者发布的消息的顺序是一致的。不考虑消息重复的情况,如果生产者发布的消息分别为 msg1、msg2、msg3,那么消费者必然也是按照 msg1、msg2、msg3 的顺序进行消费的。
RabbitMQ 的消息顺序性会被打破的情况(zc)。

RabbitMQ,相关杂

生产者一样和消费者同样需要考虑线程安全的问题。

4.x 版本弃用 QueueingConsumer。

要衡量 RabbitMQ 当前的 QPS只需看队列的即可。在实际业务应用中,需要对所创建的队列的流量、内存占用及网卡占用有一个清晰的认知,预估其平均值和峰值,以便在固定硬件资源的情况下能够进行合理有效的分配。

RabbitMQ 的消费模式分两种:推(Push)模式和拉(Pull)模式。推模式采用 Basic.Consume进行消费,而拉模式则是调用 Basic.Get 进行消费。

mandatory 和 immediate 这两个参数】—见书p56页
mandatory 和 immediate 是 channel.basicPublish 方法中的两个参数。它们都有
当消息传递过程中不可达目的地时将消息返回给生产者的功能。RabbitMQ 提供的备份交换器
(Alternate Exchange)可以将未能被交换器路由的消息(没有绑定队列或者没有匹配的绑定)存
储起来,而不用返回给客户端。

过期时间(TTL)】
TTL,Time to Live 的简称,即过期时间。RabbitMQ 可以对消息和队列设置 TTL。

死信队列】DLX,全称为 Dead-Letter-Exchange,可以称之为死信交换器,也有人称之为死信邮箱。当消息在一个队列中变成死信(dead message)之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。

延迟队列】延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。

优先级队列】
优先级队列,顾名思义,具有高优先级的队列具有高的优先权,优先级高的消息具备优先被消费的特权。

消费者Broker 中订阅并接收消息,经过可能的解包处理得到原始的数据,之后再进行业务处理逻辑。这个业务处理逻辑并不一定需要和接收消息的逻辑使用同一个线程。消费者进程可以使用一个线程去接收消息,存入到内存中,比如使用 Java 中的 BlockingQueue。业务处理逻辑使用另一个线程从内存中读取数据,这样可以将应用进一步解耦,提高整个应用的处理效率。

由于消息分发的机制,有可能某些消费者任务繁重,而某些其他消费者很快地处理完了分配的消息,如何处理这种情况:channel.basicQos(int prefetchCount)方法。

Kafka

Kafka的重复消费问题以及处理方法】
https://www.bilibili.com/video/BV1FE411y79Y?p=16&vd_source=8854a0d113bd89fb4bed316d7012dd8a

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abner_iii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值