消费者组consumer group详解-Kafka从入门到精通(九)

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 消费者组consumer group详解-Kafka从入门到精通(九)

上篇文章说了,kafka可以通过实现partitioner自定义分区,producer拦截器,拦截器是在producer发送消息之后,回调之前调用,里面主要重写两个方法,一个是onSend,可以重新定义发送的消息,一个是在回调之前调用,onAcknowledgement在回调之前调用,可以记录发送成功或者失败的消息数量。无消息丢失配置,首先保证一个问题,消息不会丢失,要acks设置为all或者-1,这样send回调才会生效,这时候还会存在一个问题,当网络瞬时故障时候,会出现乱序发送,乱序的出现是因为retries重试,这时候必须只能在同一时刻在同一个broker只能发送一次,max.in.flight.request.per.connection。还有参数replication.factory三备份原则,Min.insync.replica至少写入多少副本。

Kafka消息分区&producer拦截器&无消息丢失(八)


一、消息压缩


压缩能显著降低磁盘占用和宽带占用,从而有效提升I/O密集型性能,不过在积压的同时,也会消耗额外的CPU,因此压缩是I/O性能和CPU资源的平衡(trade-off)。

Kafka在0.7.0版本开始支持压缩特性,producer能将一批消息压缩成一条消息发送,而broker将这个压缩消息写入本地日志文件。当consumer接受到这条消息时候,会对消息进行解压,还原成初始的消息返回给用户。总结就是,producer压缩,broker保持,consumer则解压。Broker端保持通常情况下不会进行解压操作,除非需要进行消息格式转换,那么broker端就需要对消息进行重新压缩。


Kafka支持的压缩算法

三种压缩算法:GZIP,Snappy和LZ4。默认情况下,kafka是不会压缩消息的,需要指定compression.type。值得一提的是,facebook开源了新的压缩算法Zstandard,效率更高,kafka也在后续计划中支持Zstandard压缩。


算法性能比较优化

熟悉kafka源码的同学大家都知道,kafka的send主要耗时在压缩消息上,所以消息压缩性能至关重要。

对于kafka而言,LZ4>Snappy>GZIP,原本LZ4和Snappy性能差不多,但是因为kafka源码 某个关键参数进行了硬编码,使得后面两位表现不够优秀。

那么到底压缩要不要使用呢,我们则需要分析服务器I/O消耗和cpu消耗对比,比如producer消耗了大量的网络宽带或者broker端cpu占用率非常高,而producer端cpu资源非常富裕,那么可以考虑为producer开启消息压缩,反之则不需要压缩消息。

其次,压缩时间也与batch大小息息相关,batch大小越大,则压缩的时间越长,不过时间增长不是线性,而是越来越平缓,如果发现压缩很慢,说明系统的瓶颈在用户主线程而不是IO发送线程,因此可以考虑增加多个用户线程同时发送消息,这样可以提升producer吞吐量。


二、多线程处理


实际环境中只使用一个用户主线程通常无法满足所需的吞吐量目标,因此需要构造多个线程或者多个进程来同时给kafka集群发送消息。

1、多线程单kafkaProducer实例

2、多线程多kafkaProducer实例

多线程单kafkaProducer顾名思义就是全局构造一个producer,然后多个线程共享一个producer,因为kafkaProducer是线程安全的,所以这种方法也是线程安全的。

优点:实现简单性能好。

缺点:1)所有线程共享一个内存缓冲区,需要较多内存。2)一旦producer某个线程崩溃导致producer实例被破坏,则所有线程都无法工作。


多线程多kafkaProducer可以在每个producer主线程中构造一个kafkaProducer,并且保证此实例在该线程中封闭,thread confinement,线程封闭是实现线程安全重要手段之一。

优点:每个用户都有专属的producer实例,缓冲区空间及一组对应的参数配置,可以进行细颗粒度调优。单个kafkaProducer崩溃不会影响其他producer线程。

缺点:需要较大的内存分配开销。


Consumer开发


Consumer是读取kafka集群中某些topic消息的应用程序,在当前kafka生态中,consumer可以由多种编程语言实现,比如常见的java、c++、go等。

在之前版本中,kafka开源时候是由scala语言编写consumer客户端,我们这里称为scala consumer或者old consumer,旧版本的consumer。随着时间推移,发现旧版本的 consumer有很多设计缺陷,例如在旧版本中如果不使用 consumer group,而直接使用low-level consumer,用户必须实现错误处理和故障转移。因此在新版本退出new consumer,由java编写。

事实上,这两个版本设计上差距极大,很多用户甚至无法区分自己使用哪个版本的consumer,特别是新版本的consumer颠覆了旧版本的管理和保存位移机制,这也是为什么当用户切换到新版本consumer之后,会抱怨kafka-manager监控框架(yahoo开源的kafka监控框架)无法监测consumer位移信息的原因。

新版本 java consumer:主要使用kafkaConsumer。(org.apache.kafka.clients.consumer.*)

旧版本 scala consumer:zookeeper Consumer ,simpleConsumer。(kafka.consumer.*)

除了要正确区分consumer版本,我们还要了解consumer分类。严格来说,kafka分类不是kafka社区已有的概念。我们只有理解他们的分类和区别,我们才可以在实际场景中选择更合理的consumer分类来帮我们完成业务需求。Consumer分为如下两大类:

消费者组:consumer group

独立消费者:standalone consumer

这里我们先了解consumer group是由多个consumer instance(消费者实例)构造成一个整体进行消费,而standalone consumer则是单独消费的。

我们在讨论consumer或者开发consumer程序时候,必须明确给出消费者上下文consumer context,即所有consumer 版本以及consumer 分类。


Consumer group(消费者组)


Kafka官方一句话是:消费者使用一个消费组名(groupId)来标记自己,topic的每条消息都只会被发送到每个订阅它的的消费者组的一个消费实例上。

总结:

1、消费者组 可以 包含多个消费者实例,也可以包含一个消费者实例。

2、对于同一个group,每条消息只发送到一个group的实例下。

3、Topic消息可以被发送到多个group中。


我们在第一章提到过,kafka同时支持基于 队列 和基于 发布/订阅 两种消息引擎模型,事实上是通过consumer group来实现对这两种模型的支持。


所有consumer实例都属于相同group---实现基于队列模型,每条消息只会被一个consumer实例处理。

Consumer实例都属于不同group---实现基于发布/订阅的模型,极端的情况每个consumer实例都设置不同的group,这样kafka消息就会广播到所有consumer实例上。

image.png


图上清晰展示了两个consumer group订阅相同的topic场景,图中topic有p0,p1,p2和p3这4个分区,分别保存在broker1和broker2上,具体分配如下,

GroupA只有两个consumer实例,每个实例消费两个分区的数据,而group B有4个consumer实例,每个实例分别消费一个分区的数据。也很好的说明了kafka为consumer分配消息时候可以做到公平分配。

那么我们为什么需要多个consumer group呢?我们把多个 consumer实例放在一个group里有什么好处吗?实际上,consumer group是用于高伸缩性,高容错性的consumer机制。组内多个consumer实例可以同时读取kafka消息,而一旦某个consumer挂了,group会立即崩溃,这时候负责的分区交给其他consumer负责,从而保证group可以正常工作。这过程我们称呼为 重平衡(rebalance)。


另外由于kafka目前只提供单个分区内的消息顺序,而不会维护全局消息顺序,因此如果用户要实现topic全局消息顺序,就只能通过让每个consumer group下只包含一个consumer实例的方式来间接实现的。


所以group特点:

1、consumer group下可以有一个或多个consumer实例,一个consumer实例可以是一个线程,也可以是运行在其他机器上的进程。

2、GroupId唯一标识一个consumer group。

3、对某个group而言,订阅topic的每个分区只能分配给该group下的一个consumer实例。(当然该分区还可以被分配给其他订阅该topic的消费组)


位移offset


这里的offset指的是consumer的,并不是分区日志中的offset。每个consumer都会维护一个位置来记录当前消费了多少消息,很多消息引擎是把记录维护在broker上,这样做的好处是实现简单,但以下有三个问题:Broker从此变成了有状态的,增加同步成本,影响伸缩性。

需要引入应答机制(acknowledgement)来确认消费。

由于要保存多个consumer的offset,必然要引入复杂数据结构,资源则会有不必要的浪费。


Kafka则选择不同的方式,让consumer group来保存offset,那么只要简单的保存长整数就可以了,而且consumer还引入了检查机制,定期对offset持久化,从而简化了应答机制。


移位提交


consumer客户端定期会向kafka集群汇报自己消费数据的进度,这一过程被称为位移提交offset commit。位移提交对consumer非常重要,不仅象征消费进度,也决定了consumer消费语义保证。

新版本和旧版本提交位移方式完全不同:旧版本会定期将位移信息提交到zookeeper下的固定节点。

随着kafka不断更新,社区发现consumer把移位提交给zookeeper不太合适,zookeeper本质是一个协调服务组件,他并不适合作为移位的存储,毕竟频繁的读写并不是zookeeper擅长的事。于是在0.9.0.0版本,kafka推出新版本consumer中,不再提交给zookeeper,而是把位移提交给kafka的内部一个topic中(__consumer_offsets),注意,这个前面有两个下划线。


__consumer_offsets:

首先这个topic通常是给新版本的consumer使用的,旧版本虽然也提供,但是需要offsets.storge=kafka设置,很少会有人这么用。

其次,这个topic是kafka自行创建的,因此用户不可以在文件下自行删除这些日志,通常情况下,这些有50个文件,打开任意一个文件都会发现他是个正常的kafka topic日志文件目录,里面至少有一个日志.log文件,和两个索引文件.index和.timeindex。只不过该日志保存的消息都是kafka一群上consumer的位移信息罢了。

可以把这个想象成kv格式消息,key则是groupid+topic+分区号,而value就是offset值,每当更新同一个key的值时候,就会在该topic写入一条最新offset数据。同时kafka会对这个进行压实操作(compact),即每个key只保存含最新的offset消息,这样避免对分区日志的修改,也控制了总体日志容量。

考虑到一个kafka生产环境可能有多个consumer或consumer group,如果这些consumer同时提交位移,则必将加重__consumer_offsets的写入负载,因此社区特意创建了50个分区,对每个group_id进行hash取模运算,从而分散到不同的分区上。

我们在后面监控相关章节会介绍如何利用_consumer_offsets定位并读取consumer group的offset。


消费者组重平衡


标题中特意强调了consumer group,如果是standalone consumer,则没有重平衡rebalance概念,所以只对consumer group奏效。

何为rebalance,这是会consumer平衡分配,比如有一个group组,他有20个consumer实例,那么他订阅了一个topic有100个分区,那么100个分区可以重平衡分给20个consumer实例,每个实例五个。新旧版本都有重平衡过程,只是有些不同。

相关文章
|
21天前
|
消息中间件 Kafka
使用kafka consumer加载数据加载异常并且报source table and destination table are not same错误解决办法
使用kafka consumer加载数据加载异常并且报source table and destination table are not same错误解决办法
|
1月前
|
消息中间件 存储 负载均衡
Apache Kafka核心概念解析:生产者、消费者与Broker
【10月更文挑战第24天】在数字化转型的大潮中,数据的实时处理能力成为了企业竞争力的重要组成部分。Apache Kafka 作为一款高性能的消息队列系统,在这一领域占据了重要地位。通过使用 Kafka,企业可以构建出高效的数据管道,实现数据的快速传输和处理。今天,我将从个人的角度出发,深入解析 Kafka 的三大核心组件——生产者、消费者与 Broker,希望能够帮助大家建立起对 Kafka 内部机制的基本理解。
78 2
|
2月前
|
消息中间件 存储 分布式计算
大数据-53 Kafka 基本架构核心概念 Producer Consumer Broker Topic Partition Offset 基础概念了解
大数据-53 Kafka 基本架构核心概念 Producer Consumer Broker Topic Partition Offset 基础概念了解
81 4
|
2月前
|
消息中间件 SQL 分布式计算
大数据-76 Kafka 高级特性 稳定性-消费重复 生产者、Broker、消费者 导致的重复消费问题
大数据-76 Kafka 高级特性 稳定性-消费重复 生产者、Broker、消费者 导致的重复消费问题
44 1
|
3月前
|
消息中间件 安全 大数据
Kafka多线程Consumer是实现高并发数据处理的有效手段之一
【9月更文挑战第2天】Kafka多线程Consumer是实现高并发数据处理的有效手段之一
334 4
|
4月前
|
消息中间件 大数据 Kafka
Kafka消息封装揭秘:从Producer到Consumer,一文掌握高效传输的秘诀!
【8月更文挑战第24天】在分布式消息队列领域,Apache Kafka因其实现的高吞吐量、良好的可扩展性和数据持久性备受开发者青睐。Kafka中的消息以Record形式存在,包括固定的头部与可变长度的消息体。生产者(Producer)将消息封装为`ProducerRecord`对象后发送;消费者(Consumer)则从Broker拉取并解析为`ConsumerRecord`。消息格式简化示意如下:消息头 + 键长度 + 键 + 值长度 + 值。键和值均为字节数组,需使用特定的序列化/反序列化器。理解Kafka的消息封装机制对于实现高效、可靠的数据传输至关重要。
116 4
|
4月前
|
消息中间件 负载均衡 Kafka
【Kafka消费秘籍】深入了解消费者组与独立模式,掌握消息消费的两种超能力!
【8月更文挑战第24天】Apache Kafka是一款高性能的分布式消息系统,支持灵活多样的消费模型以适应不同的应用场景。消息按主题组织,每个主题可划分为多个分区,确保消息顺序性。本文深入探讨了Kafka中的两大核心消费模式:消费者组(Consumer Group)和独立消费者(Standalone Consumer)。消费者组允许多个消费者协同工作,实现负载均衡及故障恢复,是最常用的消费模式。独立消费者模式则适用于需要高度定制化处理逻辑的场景,如消息重放等。通过对比这两种模式的特点和提供的示例代码,开发者可以根据具体需求选择最合适的消费策略,从而更好地利用Kafka构建高效的数据流应用程序。
130 3
|
4月前
|
消息中间件 监控 Java
【Kafka节点存活大揭秘】如何让Kafka集群时刻保持“心跳”?探索Broker、Producer和Consumer的生死关头!
【8月更文挑战第24天】在分布式系统如Apache Kafka中,确保节点的健康运行至关重要。Kafka通过Broker、Producer及Consumer间的交互实现这一目标。文章介绍Kafka如何监测节点活性,包括心跳机制、会话超时与故障转移策略。示例Java代码展示了Producer如何通过定期发送心跳维持与Broker的连接。合理配置这些机制能有效保障Kafka集群的稳定与高效运行。
97 2
|
4月前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
100 0