基本认知
前世今生
信息队列(Message Queue)是一种常用的分布式系统间通信方式,用于解耦和异步处理不同系统之间的消息传递。
在很早的计算机系统中,信息队列并不常见。信息传递主要通过共享内存、文件或者直接的网络请求等方式进行。但这些方式存在一些问题,比如实时性差、高并发场景下的性能瓶颈以及系统间的强耦合等。 下面模拟一个购物场景来进行演示
使用场景
用户搜索商品→搜索行为记录→点击商品→点击行为记录
其中搜索和点击行为记录会被执行记录存储 该流程高度依赖记录存储程序所在的机房的稳定性
解决方案:解耦 通过设计消息队列接收搜索和点击行为记录再传输进行记录存储,减轻对记录存储一环的依赖性
多个用户发起订单,处理订单响应能力不足
解决方案:削峰
设计消息队列接收用户订单数据,按处理订单能力进行顺序处理,减轻处理订单程序负载
用户发起订单→库存记录-1→订单记录+1→通知商家
该链路耗时时间过长
解决方案:异步
设计消息队列,在用户发起订单后同时进行库存记录,订单记录和通知商家的环节,节省线路损耗时间
由于负责存储机房故障,没有日志以供修复
解决方案:将日志通过消息队列经日志组件下载到搜索引擎使用Kibana展示
优势
通过以上场景的模拟,可知消息队列可被应用于对解耦,削峰,异步,日志处理要求高的领域。
解耦:消息队列可以将不同组件或模块之间的通信通过异步消息传递的方式进行解耦。组件之间不直接依赖于对方的存在和可用性,而是通过发送和接收消息进行通信。这样可以降低组件之间的紧密耦合,使其能够独立地进行开发、演化和部署。
削峰:在高并发场景下,消息队列可以起到削峰平谷的作用。当系统的请求量超过处理能力时,请求可以先存储在消息队列中,然后按照系统的处理能力逐步消费和处理。这种方式可以平滑处理峰值流量,避免系统过载和崩溃。
异步:消息队列支持异步消息传递,发送方将消息发送到队列中后就可以继续执行其他操作,而无需等待接收方的响应。接收方可以在适当的时候从队列中获取消息并进行处理。这种异步通信方式可以提高系统的响应速度和吞吐量,尤其适用于需要长时间处理、耗时的操作或需要与外部系统进行集成的情况。
日志处理:消息队列可以作为日志处理的中间件,将日志消息写入队列后由消费者进行处理。这样可以实现异步的日志记录和处理,避免由于日志写入导致的性能损耗,并可以将日志与核心业务逻辑解耦。
kfka的使用
平台提供的公共系统服务诸如订单服务和支付服务会被转化为数据和日志信息与用户行为一同传输到消息队列中,下面以Kafka为例:
架构:
Producer(生产者)
Cluster(物理集群) 涵盖若干数量Topic(逻辑队列),Topic下有若干Partition(分片),Partition下有若干Replica(副本),一个Leader replica(领导者副本)和零个或若干个follower replica(追随者副本),Offset是消息在partition中的相对位置信息(相当于唯一的ID,严格递增)
ConsumerGroup(消费者组) 涵盖若干数量Consumer(消费者)
领导者副本(Leader Replica):每个分区都有一个领导者副本,它负责处理该分区的所有读写请求。生产者将消息发送到领导者副本,消费者从领导者副本读取消息。领导者副本负责消息的持久化,并将消息复制到追随者副本。
追随者副本(Follower Replica):追随者副本是对领导者副本的备份,用于实现数据的冗余和容错。它完全复制了领导者副本中的消息数据。追随者副本不直接处理读写请求,而是与领导者副本进行数据同步,保持与领导者副本的一致性。
Broker(代理),运行 Kafka 服务器的节点,即 Kafka 集群中的一个实例。每个 Broker 负责接收、存储和转发消息,参与消息的生产和消费过程。Controller 是负责管理集群的整体协调和控制的特殊角色。在 Kafka 集群中只会有一个 Broker 作为 Controller。
具体使用
发送消息
producer→broker→consumer
当生产者发送消息时,可以选择将多个消息打包成一个批次(batch)进行发送,而不是逐条发送。从而实现消息的批处理
当消息量很大时,可以选择将打包好的批次压缩后再进行发送。
查找文件,
一般使用二分法找到小于offset的最大索引位置 时间戳索引查找:
- 创建一个二级索引字典(或哈希表),用于存储时间戳和对应的索引位置。
- 遍历时间戳索引,将每个时间戳和对应的索引位置添加到二级索引字典中。
- 对二级索引字典进行排序,按照时间戳的升序进行排序。这可以提高后续的查找效率。
- 要进行时间戳的查找,使用二分法在排序后的二级索引字典中查找小于或等于目标时间戳的最大时间戳。
- 根据找到的最大时间戳,在时间戳索引中找到对应的索引位置,并返回结果。
数据拷贝
传统数据拷贝: 涉及多次数据复制,从磁盘读取到内核空间,然后再从内核空间复制到用户应用空间。这种拷贝操作会增加CPU和内存的使用,并且可能导致性能瓶颈。
零拷贝: 数据可以直接从内核空间发送到网络接口卡(NIC)的缓冲区,无需额外的拷贝操作。这样可以减少了CPU和内存的使用,提高了数据传输的效率和性能。
分配
手动分配
完全依赖业务,不能自动容灾,后期拓展需要停止业务,成本较高
自动分配
选取broker作为Coordinator(调停者)负责资源的协调和管理,找到Coordinator,加入集群界面与Coordinator建立通信,再选取leader进行统一的分配策略,同步集群方案,各个节点向Coordinator发送心跳请求以维持连接和转台同步。
心跳请求是指在分布式系统中,节点之间周期性地发送用于确认活动状态的请求。它的目的是确保节点的健康运行和相互之间的通信。如果发送节点在一段时间内没有收到来自其他节点的心跳响应,则可以将其视为不可用或发生故障。这时通常会触发一些节点恢复或故障处理机制,如重新选举领导者或重新分配资源等。
缺点:
- 不同节点的数据复制占用开销大
- 重启操作、升级迭代、新leader设置、替换、扩容、缩容带来的数据差异问题和时间成本问题
- 负载不均衡(而且在解决一个分区的IO问题又要解决另一个分区的IO问题,解
- 决方案复杂)
- 没有自觉的缓存,完全依赖PAGE CACHE
- Controller Coordinator和Broker同一进程,面对大量IO操作会造成性能问题
BMQ
兼容Kafka协议,可以将Kfaka无缝迁移到BMQ上不需要太多的改动, 存算分离,将存储和计算两个关键组件进行了分离,提高了效率 云原生消息队列, 可以方便地部署和管理
架构
Producer→Proxy Cluster→Broker Cluster→
Distributed Storage System→Proxy Cluster→Consumer Group
对比Kafaka重启、替换、扩容、缩容带来的数据复制和时间成本,BMQ可以直接对外服务,秒级完成
写入流程
文件结构:
BMQ将数据分片存储在不同的DataNode上。在写入数据时,BMQ会随机选择一定数量的DataNode进行写入操作。这样可以实现数据的分布式存储,提高系统的吞吐量和容错性。
BMQ会对partition的Segment随机均匀的分配到不同节点上。每个节点都会负责存储一部分partition的Segment,从而实现数据的分散存储,避免Kafaka数据过于集中在某个节点上的情况。
Broker-Partition状态机机制:
保证任意分片在同一时刻只能在一个Broker上存活。当某个Broker失效或需要重新平衡集群的负载时,BMQ将触发分片的迁移。迁移过程中,BMQ会先选择目标Broker作为迁移的接收方,然后将需要迁移的分片数据复制到目标Broker上。迁移完成后,BMQ会更新分片的元信息,确保该分片只在目标Broker上存活。
写入流程
- Producer发送消息:Producer向Broker发送消息,消息内容包括消息体和相关元数据。
- 消息校验:Broker接收到消息后进行校验,包括对消息的格式、大小、合法性等方面进行验证。如果消息校验通过,则进入下一步;否则,Broker可能会丢弃无效的消息或者发送错误响应给Producer。
- 内存buffer缓存:经过校验后,有效的消息会暂时存储在内存的buffer中,等待后续的持久化写入。
- Write Thread写入磁盘:BMQ会通过一个专门的Write Thread来将buffer中的消息行写入磁盘。
- Checkpoint过程:在写入过程中,BMQ会定期进行Checkpoint操作,将已经写入磁盘的消息行的位置信息保存下来。
Write Thread(写线程)
负责将数据行按照预定义的格式进行持久化写入,并建立相应的索引和元数据。具体的流程如下:
- Write Data Flush(写数据刷新):将内存缓冲区中的数据刷新到磁盘中,以确保数据的持久化存储。
- 索引建立:在写入数据时,同时构建相应的索引结构,以支持高效的数据检索和查找。
- Checkpoint(检查点):CheckPoint会记录当前的写入位置、索引信息和其他元数据,并持久化到稳定存储介质中,以便于系统恢复时能够准确定位到上一次的写入位置。
- Roll new Segment file(滚动新的段文件):关闭当前的段文件,并创建一个新的段文件用于存储后续的写入数据。
BMQ采用段文件的方式存储消息数据。当一个段文件达到一定大小或者时间限制时,Write Thread会触发滚动新的段文件操作。滚动新的段文件
Failover(故障转移)
在写入数据时,如果当前节点发生故障或不可用,系统会自动转移到其他可用的DataNode节点上进行写入。这个过程是为了确保数据的可靠性和高可用性。
读取数据
当向服务器发出fetch request后,经wait等待
- 缓存查询:BMQ首先会在缓存中查找请求的数据。如果请求的数据已经在缓存中,即为数据命中,BMQ会直接从缓存中读取相应的数据,并将其返回给客户端。如果数据没有命中缓存,那么BMQ会进行磁盘读取操作。
- 文件打开和定位:首先,BMQ会打开相应的文件,并将文件指针(文件读写位置)定位到请求的数据所在的位置。
- 磁盘读取:一旦文件指针定位到正确的位置,BMQ会从磁盘上读取相应的数据块,并将其返回给客户端。
多机房部署
多机房部署BMQ时,全量分区是一种常见的策略。在这种部署方案中,BMQ集群将数据分为多个分区,并将每个分区的副本分布在不同的机房中,以增加系统的可用性和容错性。
在全量分区的情况下,proxy(代理)会将每个分区均匀地分配给所有的broker(代理节点)。每个broker都会负责处理部分分区的读写请求,并维护分区的数据副本。
高级特性
泳道消息
将具有相似特征或相同业务逻辑的消息进行分组,每个泳道负责处理特定类型的消息。这样可以提高系统的可扩展性和灵活性,使得不同的消费者可以独立并行地处理自己关注的消息,而不会相互干扰。
Databus数据总线
Databus(数据总线)可以帮助简化消息队列客户端的复杂性,并解耦业务逻辑与消息主题。通过引入Databus,可以将消息的发布和订阅逻辑抽象出来,使得客户端代码更加清晰和可维护。
同时,Databus还具备高吞吐量和可靠的消息传递机制。它能够处理大量并发的消息请求,有效地缓解集群的压力,并提供可靠的消息传递保证。这意味着即使在高负载情况下,Databus仍然能够稳定地处理和传递消息。
mirror
mirror是一种数据复制和同步的工具,可以帮助解决跨Region读写问题。它通过最终一致性的方式将数据从一个地理区域复制到另一个地理区域,以实现数据的跨Region读写。
Parquet
Parquet是一种列式存储格式,它在处理大规模数据分析和查询时具有很高的效率和压缩比。
- 数据结构化:将原始数据进行结构化处理,将其转换为适合Parquet存储的格式。
- Parquet Engine:使用Parquet Engine或相关的工具,将结构化后的数据按照Parquet格式进行转换和构建。
- 选择合适的构建方式:在构建Parquet格式文档时,可以选择按行追加(append)的方式构建,也可以选择批量写入(batch write)或者增量更新(incremental update)的方式。
RocketMQ
RocketMQ是一个分布式的消息中间件系统,具有高吞吐量、低延迟、可靠性强等特点
tag:Tag是用于对消息进行标记和分类的一种属性。它可以被用来过滤和选择特定类型的消息进行消费message queue:消息队列,RocketMQ采用消息队列的方式进行消息传输和存储。nameserver:RocketMQ的NameServer,它负责维护整个RocketMQ集群的元数据信息,包括Broker、Topic和Producer等的路由信息。
存储模型:
RocketMQ的存储模型基于Broker,主要包括以下几个组件:
- CommitLog:它是消息的物理存储,用于持久化生产者发送的消息。每个 Broker 都有自己的 CommitLog 文件。当消息发送到 Broker 时,会被追加写入 CommitLog 中。
- Consume Queue:消费队列是逻辑上的存储单元,用于存储消息的消费进度信息。每个 Topic + 分区(Partition)对应一个消费队列,用于记录消息被消费的进度和位置。
- Index File:索引文件记录了消息在 CommitLog 中的偏移量以及对应的消息 key。这样可以根据消息的 key 快速定位到消息的位置。
- Checkpoint File:检查点文件记录了消费者消费进度的信息,用于恢复消费者在故障恢复后的消费位置。
分区和消费队列:CommitLog中的消息会根据分区规则分发到不同的消费队列中。
高级特性:
RocketMQ提供了一些高级特性,以满足复杂应用场景的需求:
事务消息:
RocketMQ支持事务消息,即在消息发送方执行本地事务逻辑后再提交消息。如果消息发送失败,RocketMQ将进行回查,保证事务的最终一致性。
延迟服务:
RocketMQ支持延迟发送消息的功能,可以通过设置定时任务,将消息在指定时间点发送。这对于一些需要延迟触发的业务场景非常有用。
重试和死信队列:
当消息消费失败时,RocketMQ会进行重试操作。如果超过最大重试次数仍然失败,消息会被发送到重试队列,以待后续的人工介入处理。
总结:
消息队列服务于当下互联网平台的用户,负责处理用户的大量请求,并接收生产者消息,通过集群的方式响应请求,提高了系统的响应速度,防止性能过载,为拓展互联网业务提供了选择。BMQ,RocketMQ,则在Kfaka的基础上提供了更加完善的服务。