Kafka

一、基本概念

Broker:Kafka 集群包含一个或多个服务器,这种服务器被称为 broker。

Topic:每条发布到 Kafka 集群的消息都有一个类别,这个类别被称为 Topic。

Partition:Partition 是物理上的概念,每个 Topic 包含一个或多个 Partition。

Producer:负责发布消息到 Kafka broker。

Consumer:消息消费者,向 Kafka broker 读取消息的客户端。

ConsumerGroup:每个 Consumer 属于一个特定的 Consumer Group(可为每个 Consumer 指定 group name,若不指定 group name 则属于默认的 group)。

Leader:每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。

Follower:Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。

Offset:kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafk

二、架构图

一个典型的Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。

三、生产者

3.1kafka消息发送流程

1.先初始化一个ProducerRecord对象,其中包含Topic和Value。key和Partion可选,初始化完成后会进行序列化

2.数据被传到分区器,如果ProducerRecord对象指定了分区直接进入下一步,否则会根据ProducerRecord对象的key选择一个分区。在确定分区后,这条记录会被添加到一个记录批次。这个批次的消息会被发送到指定的主题和分区上

3.服务器收到消息后返回相应,如果消息写入成功,返回一个RecordMetaData对象,包含主题和分区信息,以及记录在分区里的偏移量;如果写入失败,会返回一个错误,生成着在收到错误后会尝试重新发送消息,重试多次失败后返回错误信息。

3.2分区策略

在发送过程中,可以指定发送的key和partion,key一方面可以作为附加悉尼下,另一方面可以用于消息路由,这里涉及到Kafka的消息发送路由机制,有三种情况:

1.指定了patition,则直接发送到指定partition;

2.未指定patition但指定key,通过对key进行hash选出一个patition。在不改变主题分区数量情况下,每次同一个key都会被映射到相同的partition

3.patition和key都未指定,使用轮询选出一个patition。

3.3 拦截器

producer拦截器(interceptor)使得用户在消息发送前后以及producer回调逻辑前有机会对喜爱做一些定制化需求,kafka允许用户指定多个interceptor按序作用于消息以形成一条拦截链。

定义一个拦截器需要实现ProducerInterceptor接口。

3.4配置参数

buffer.memory

设置生产者内存缓冲区大小,用户缓存要发送到服务器的消息,如果应用程序发送消息操作速度超过发送到服务器的处理响应速度,可能会导致生产者缓存不足,这时send方法调用会被堵塞或抛异常

compression.type

默认情况,消息发送不会被压缩,可以通过这个参数指定使用snappy,gzip,lz4等压缩算法来降低网络传输开销和内存开销

retres

当生产者向服务器发送消息失败时,会尝试根据此配置次数进行消息重复,如果超过配置次数仍失败,生产者会放弃重试并返回错误。默认情况,生产者会确保每次重试间隔大于100ms,可以通过retry.backoff.ms修改。

batch.size

当有多个消息需要被发送到同一个分区时,生产者会将他们放在同一个批次里,该参数指定了一个批次可以使用的内存大小,按照字节数计算,而非消息个数。当批次被填满时,批次里的消息会被发送出去。实际生产者不一定会在批次满后才发消息,因此设置得很大不会导致消息延迟,但会占用更多的内存。如果设置的过小,会导致生产者需要更频繁地发送消息。

linger.ms

指定生产者在发送批次之前,等待更多消息加入批次的时间,生产者会在批次被填满或达到linger.ms时把批次发送出去。可以通过调整linger.ms取到吞吐量和资源开销之间的平衡。

max.request.size

指定生产者在单个批次里能够发送消息的最大值,最好和broker对应的可接受的消息最大值匹配,避免生产者发送的消息被broker拒绝

max.in.flight.requests.per.connection

指定生产者在收到服务器响应前可以发送多少个消息,值越高,则吞吐量越高,但占用内存也越多,同时还会有在消息发送失败时乱序的风险。将值设为1,可以保证消息是按照发送的顺序写入服务器的,即使发生了重试。

四、消费者

4.1消费者和消费组

生产者和消费者往往是一对多的关系,多个消费者可以形成一个消费组来订阅主题消息,对消息进行分类。一个消费组中订阅的都是同一个主题,每个消费者接受主题一部分分区的消息。同一个消费组内的不同消费者只能订阅一个主题下不同分区的消息,不同消费组可以订阅同一个主题的统一分区消息

对于消费组,有5个状态

Empty:消费组下没有任何活跃的消费者,可能为消费组刚创建的时刻或工作一段时间后所有消费者离开。

PreparingRebalance:表明group正在准备进行group rebalance。此时group收到部分成员发送的JoinGroup请求,同时等待其他成员发送JoinGroup请求,知道所耦成员都成功加入组或超时。

AwaitingSyc:表明所有成员都已经加入组并等待leader consumer发送分区分配方案。

Stable:表明group开始正常消费,可以响应客户端发送的任何请求

Dead:表明group已经彻底废弃,group内没有任何active成员且group的所有元数据都已被删除。

4.2rebalance 分区再均衡

基于一个消费组订阅特定主题的情况下,消费者数量或主题分区数量发生变化都会引起分区再均衡。在再均衡期间,消费者无法读取消息,会造成整个群组一小段时间的不可用。另外,当分区被重新分配到另一个消费者时,消费者当前的读取状态会丢失,可能需要去刷新缓存,在消费者重新恢复状态之间会拖慢应用程序。

4.2.1rebalance触发时机

消费者通过被指派为群组协调器的broker(不同的群组有不同的协调器)发送心跳来维持他们和群组的从属关系以及他们对分区的所有权。只要消费者以正常的时间间隔发送心跳,就被认为是活跃的,说明还在读取分区的消息。消费者会在轮询尝试获取新消息或提交偏移量时发送心跳,如果消费者停止发送心跳的时间足够长,会话就会过期,群组协调器认为消费者宕机,会触发一次再均衡。另一方面,我们在主动清理消费者时,消费者也会通知协调器它即将离开群组,也会触发一次再均衡。

具体而言,可以分为以下三种情况:

1.组成员发生变更:新consumer加入,原有consumer主动离开或崩溃

2.组订阅topic数发生变更,基于正则订阅,新主题创建,命中正则规则

3.组订阅topic的分区属发生变更,如通过脚本增加订阅topic的分区数

4.2.2rebalance 分区分配策略

kafka新版本提供了3种分配策略,用于决策归属topic的每个分配会被分配给哪个消费者,具体有:

1.range策略:基于范围的思想,将单个topic的所有分区按照顺序排列,然后把这些分区划分成固定大小的分区段并依次分配给每个消费者。

2.round-robin策略:把所有topic的所有分区顺序白开,轮训式地分配给每个消费者

3.sticky策略:采用“有黏性”策略对所有消费者实例进行分配,可以规避在极端情况下的数据倾斜并在两次rebalance间最大限度维持之前的分配方案。

4.2.3rebalance generatian

每次触发再均衡后,有一个标志再均衡代数的变量,会在每次触发再均衡后+1。主要用于保护consumer group,尤其是防止无效offset的提交。比如上一代的consumer成员由于某些原因延迟提交了offset.但再均衡后该group产生了新一届的group成员,而这次延迟的offset提交携带的是旧的generation信息,则这次提交会被拒绝。

rebalance协议

rebalance本质是一组协议,由group和coordinator(协调者)共同完成,其中coordinator是每个组的一个协调者,负责对组的状态进行管理,主要职责是再均衡时促成组内所有成员达成新的分区分配方案。再均衡协议包含以下协议请求:

JoinGroup请求:consumer请求加入组

SyncGroup请求:group leader把分配方案同步更新到组内所有成员

Heartbear请求:consumer定期向coordinator汇报心跳表名自己依然存活

LeaveGroup请求:consumer主动通知coordinator该consumer即将离组

DescribeGroup请求:查看组的所有信息,包括成员信息、协议信息、分配方案以及订阅信息等。主要供管理员使用。

在rebalance过程中,coordinator主要处理consumer发来的JoinGroup和SyncGroup请求。当consumer主动离组时会发送LeaveGroup给coordinator。

在成功rebalance后,组内所有consumer定期向coordinator发送Heartbeat请求。而consumer则根据Heartbeat请求的响应中是否包含REBALANCE_IN_PROGRESS来判断当前group是否开启新一轮rebalance。

rebalance流程

再均衡的流程分为以下几步:

找到coordinator:确定协调者的算法如下:

计算Math.abs(groupID.hashCode) % offsets.topic.num.partitions参数值(默认是 50) ,假设是 10。

寻找一consumer_offsets分区 10的 leader副本所在的 broker,该 broker即为这个group的 coordinator。

收集consumer,选举Leader并制定分配方案:组内所有consumer向coordinator发送JoinGroup请求,coordinator从中选择一个(通常是第一个)作为leader。并把所有成员信息以及他们的订阅信息发给leader,由leader负责为整个group的所有成员制定分配方案

同步更新分配方案:所有消费组发送SyncGroup请求给coordinator,但只有leader会将制定的分配方案封装进SyncGroup请求发送给coordinator。coordinator从leader请求中把属于每个consumer的方案单独抽取出来,作为SyncGroup请求的response返还给各自的consumer。

4.3消费者配置

除了上面几个配置外,消费者还有一些核心配置,通过这些配置有助于我们更好地理解消费者的运行逻辑。

fetch.min.bytes

指定消费者从服务器获取记录的最小字节数。broker在收到消费者的数据请求时,如果可用的数据量小于配置指定的大小,会等有足够的数据再一起返回给消费者,以此降低消费者和broker的工作负载。

fetch.max.wait.ms

指定broker在没有收到足够数据时的最大等待时间,默认500ms,如果没有足够的数据流入broker,即使消费者尝试获取数据,broker也不会立即返回,而会等待离上次拉取数据时间间隔fetch.max.wait.ms才会返回给客户端。这个配置设置过大,会导致数据消费延迟,但可以降低消费者和broker的工作负载

max.partition.fetch.bytes

指定服务器从每个分区里返回给消费者的最大字节数。默认为1MB。这个配置值必须比broker能够接受的最大消息的字节数(max.message.size配置)大,否则消费者可能无法读取过大的消息,导致消费者一直刮起重试。另外还需要考虑消费者处理的时间,如果单词poll数据太多,消费者处理可能无法及时进行下一个轮询来避免会话过期。

session.timeout.ms

该属性指定了消费者在被认为死亡之前可以与服务器断开连接的时间,默认为3s,如果消费者没有在指定时间内发送心跳给群组协调器,会被认为死亡,群组协调器会触发再均衡,把它的分区分配给其他消费者。阈值关联的另一个配置是heartbeat.interval.ms,用来指定poll()方法向协调器发送心跳的频率。因此两个属性一把你需要同步修改,如session.timeout.ms是3s,则heartbeat.interval.ms应该是1s。将session.timeout.ms设置更小一些,可以更快地监测和恢复崩溃节点,但可能会导致非预期的再均衡。

auto.offset.reset

指定消费者在读取一个没有偏移量的分区或者偏移量无效(银消费者长时间失效,包含偏移量的记录已经过期或被删除)的情况下的处理动作,有两个值:

latest,默认值,从最新记录开始读取

earliest,从起始位置读取分区记录

enable.auto.commit

指定消费者是否自动提交偏移量,默认为true。为了尽量避免出现重复数据和数据丢失,可以设为false,有自己控制何时提交偏移量,如果设为true,则可以通过配置auto.commit.intervall.ms来控制提交的频率

partition.assignment.strategy

分区分配策略,决定哪些分区由哪些消费者消费,有两种默认策略:

Range:把主题的若干个连续分区分配给消费者,如有分区p1,p2,p3分配给消费者c1,c2,则分配结果可能为c1->p1,c1->p2,c2->p3

RoundRobin:把主题逐个分配给消费者。如对于上例,分配结果为c1->p1,c2->p2,c1->p3。RoundRobin策略保证所有消费这分配相差0或1个数量的分区。

sticky:采用“有黏性”的策略对所有的consumer实例进行分配,可以规避极端情况下的数据倾斜并且在两次rebalance间最大限度地维持原有的分配方案,相对上面两种方案,有效避免了无视历史分配方案的缺陷。

4.4提交和偏移量

消费者需要更新自己在分区消费的记录偏移量,这个操作叫做提交。通常,偏移量是下一条带消费的消息的位置。消费者提交的偏移量作用在于当消费者发生崩溃或有新消费者加入群组引发分区再均衡时,当分区被分配到新的消费者时,新的消费者可以根据分区记录的偏移量来继续消费消息。这里有两种异常情况:

1.如果提交的偏移量小于客户端处理的最后一个消息的偏移量,则两个偏移量之间的消息会被重复处理。

2.如果提交的偏移量大于客户端处理的最后一个消息的偏移量,则两个偏移量之间的消息会被丢失

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值