Kafka相关概念总结

一、Kafka是什么

概念:Kafka是一个多分区、多副本,且基于zookeeper协调的分布式消息系统,也是一个分布式流式处理平台,它的特点包括:高吞吐、可持久化、可水平扩展、支持流数据处理等。

Kafka扮演的三大角色:
消息系统:具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等。
存储系统:把消息存储到磁盘中,相比于其他基于内存存储的系统而言,可以有效的降低消息丢失的风险。
流式数据处理平台:kafka不仅为每个流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理类库。

二、Kafka中的相关名词

producer:生产者,消息的发送方

consumer:消费者,消息的接收(消费)方

consumer Group(CG):消费组,由多个consumer组成,消费组内的每个消费组都负责消费不同分区的数据,在同一个消费组内,一个分区只能由一个消费组消费,消费组之间互不影响,所有的消费组都属于某个消费组,即消费组是逻辑上的一个订阅者

broker:服务代理节点。对于kafka而言,broker可以简单的看作一个独立的Kafka服务节点或kafka服务实例,一个kafka集群由多个broker组成,一个broker,可以容纳多个topic

topic:主题,Kafka中的消息以主题为单位进行归类,生产者负责将消息发送到特定的主题(发送到kafka集群中的每一条消息都要指定一个主题),而消费者负责订阅主题,并进行消费

partition:分区,和topic-主题一样,都是逻辑上的概念,一个主题可以细分为多个分区,一个分区只属于一个主题,同一主题下不同分区包含的消息是不同的,分区在存储层面可以看作可追加的日志文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)

offset:偏移量,是消息在分区中的唯一标识,如果说把分区看作一个数组,offset就像数组的index,Kafka通过它在保证消息在分区内的顺序性,不过offset并不跨区,也就是说Kafka保证的是分区有序,而不是主题有序。

replica:副本,副本是建立在Kafka集群的基础上的,可以理解为是某个分区的备份,注意:副本的数量不要大于broker的数量,副本包括,leader和follower,Kafka的读写操作都发生在leader上,leader负责把数据同步给follower,当leader挂了,会通过主从选举,从多个follower中选举产生一个新的leader

AR:一个partition的所有副本(就是replica,包括leader和follower)
ISR:可以和leader同步或者已经同步了(保持同步)的follower+leader本身组成的集合,一旦不能保持同步(也就是性能变差)后,就会被踢出ISR集合
OSR:不能和leader保持同步的集合
也就是说AR=ISP+OSR
注意:消费组中的消费者数量不能比topic中的partition的数量多,否则多出来的消费者消费不到消息

三、生产者端发送消息相关

生产者端同步发送消息:
如果生产者发送消息后,没有接收到来自kafka返回的ack,生产者会阻塞(阻塞时间可以通过spring.kafka.producer.properties.retry.backoff.ms进行配置),如果阻塞时间过了,还没有收到,会进行重试,(重试发送的次数可以通过spring.kafka.producer.retries进行配置)

生产者端异步发送消息:
生产者发送完消息以后就可以执行之后的业务,broker在收到消息后,异步调用生产者提供的callback回调方法,注意:异步发送消息,更有可能导致消息的丢失。
生产者同步发送消息时,acks相关配置:

acks=0,则不需要任何broker收到消息,直接返回ack给生产者(这种方式效率最高,但也最容易丢消息)。

acks=1,则消息必须发给多副本中的leader,且需要等leader收到后,将消息写入到本地的log中,才会返回ack给生产者(这种方式相对而言兼顾了性能和安全)。

acks=-1/all,则需要等待最小同步副本数量(min.insync.replicas,默认是1.推荐配置大于等于2,小于等于副本数量),这个参数配置的副本数量都成功写入log(同步完成),才会返回ack给生产者(这种策略保证了只要有一个备份存活,消息就不会丢失,这种方式很安全,但是也意味着相对而言性能就没那么强了)。

四、消费者长轮询poll消息

默认情况线下消费者一次会poll500条消息
spring.kafka.consumer.max-poll-records=500
同时还可以通过
spring.kafka.consumer.properties.max-poll-interval-ms=30*1000(默认30s)
设置消费者每次从Kafka拉取数据的时间间隔,如果两次poll的时间超过30s的时间,kafka会认为其消费能力过弱,将其踢出消费者,将该分区分配给其他消费者

需要注意的是:对于长轮询来说,如果一次poll到了500条,那么可以直接执行下一步代码,如果没有到500条,且时间还在设定的时间内的,那么长轮询将继续去poll,要么到最大拉取数量,要么到最大拉取间隔时间,才会进入下一步逻辑.

五、关于消费者提交offset

1、提交的内容:
消费者无论是手动还是自动提交,都需要把所属的消费组consumer+消费的哪个主题topic+消费的哪个分区partition和对应的偏移量offset,这些信息提交到集群的_consumer_offfsets主题里。

2、自动提交:
消费者poll(拉取)到消息后,就自动提交,不在乎是否已经消费完。
spring.kafka.consumer.enable-auto-commit=true(默认)
注意:自动提交也会将丢失消息的风险加大,例如,此时有一条消息是创建一个新的订单,消费者接收到这个消息后,还没等录入到数据库,就直接提交offset了,此时刚好消费者挂了,那么下次启动时,offset已经跳过了这条消息,而数据库中也没有录入成功,这就造成了消息的丢失。

3、手动提交:
spring.kafka.consumer.enable-auto-commit=false
手动提交也分为了两种:
手动同步提交,在消费完消息后调用同步提交的方法,,当集群返回ack之前一直阻塞,返回ack后表示提交成功,再执行之后的逻辑。
手动异步提交,再消息消费完之后提交,不需要等到集群返回ack,直接执行之后的逻辑,可以设置一个回调方法,供集群调用。
设置了spring.kafka.consumer.enable-auto-commit=false为手动提交后,
我们往往还可以通过spring.kafka.listener.ack-mode来指定提交方式
AckMode主要包含如下:
模式解释
RECORD在监听器处理每个记录后提交
BATCH在下一次轮询之前提交已处理的内容
TIME在ackTime过后提交挂起的更新
COUNT超过ackCount后提交挂起的更新
COUNT_TIME在超过ackCount或者ackTime后提交
MANUAL用户使用AcknowledgingMessageListener对acks负责,当每一批(max-poll-records可以自定义一批拉取数量)poll的数据被消费监听器处理以后,才会提交
MANUAL_IMMEDIATE用户使用AcknowledgingMessageListener对acks负责,使用者立即处理提交,也就是说用户一调用即提交。

六、新消费组的消费offset规则

新消费组中的消费者在启动以后,会默认从当前分区的最后一条消息的offset+1处卡hi是消费(也就是消费新的消息)。可以通过以下配置,让新的消费者第一次从头开始消费,之后开始按offset进行消费。
spring.kafka.consumer.auto-offset-reset:earliest

earliest:当各个分区下有该消费者提交的offset时,从该消费者提交的offset开始消费,如果没有,则从头消费
latest(默认):当各个分区下有该消费者提交的offset时,从该消费者提交的offset开始消费,如果没有,则消费新的消息
none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常

七、kafka集群中的controller、rebalance、HW

controller

集群中谁来充当controller?
每个broker启动时,会像zookeeper创建一个临时序号节点,获得序号最小的哪个broker将会作为集群中的controller。

controller的作用是管理集群中所有分区和副本的状态:
当集群中有一个分区中各个副本的leader挂掉,需要在集群中选举出一个新的leader,选举的规则是从ISR集合中最左边获得。
当集群中有broker新增或减少,controller会同步信息给其他broker。
当集群中有分区新增或者减少,controller会同步信息给其他broker。
rebalance机制(重新平衡机制)

前提是:消费者没有指明分区进行消费,当消费组里消费者和分区的关系发生变化,就会触发rebalance机制。这个机制会重新调整消费者消费哪个分区,达到相对平衡的一个效果。

在触发rebalance机制之前,该分区由哪个消费者消费有三种策略:
1、range:通过公式计算(前面的消费者是分区总数/消费者数量 + 1,之后的消费者是分区总数/消费者数量),分区归哪个消费者消费。
2、轮询:大家轮流消费,多个分区,轮流发给多个消费者消费(相当于轮流发牌)。
3、sticky:粘合策略,在触发了rebalance后,在消费者消费的原分区不变的基础上进行调整,如果这个策略没有打开,那么就要进行全部的重新分配,建议开启。
HW和LEO

HW,俗称高水位,取一个partition对应的ISR中最小的LEO(log-end-offset,也就是同步到所有ISR集合中副本的最后一条消息的offset)作为HW(所以可以理解为,高水位就是ISR中副本完成同步的消息的offset)。
consumer最多只能消费到HW所在的位置。
每个副本(replica)都有HW,leader和follower各自负责更新自己的HW的状态,对于leader新下入的消息,如果还没有和ISR中的副本完成同步的话,consumer不能立即消费,leader会等待该消息被ISR中所有副本同步后更新HW,此时才能被consumer消费。
这样的机制可以在一定程度上保证消息不容易丢失,也就是说,同步后,如果leader所在的broker挂了,该消息仍然可以从新选举的leader中获取。

八、如何防止消息丢失

思路:想要消息不丢失,那么肯定需要从发送到接收都不能出错,所以如下:

生产者:
1、使用同步发送,因为异步发送不等kafka返回ack,容易丢失;
2、设置ack为1或者all(-1),如果设置为all,min.insync.replicas设置为需要同步的副本数量,这样一来,即使leader挂了,也可以在新的leader中找到备份的消息。

消费者:
把自动提交改为手动提交,即设置enable-auto-commit=false,因为自动提交也有可能丢失数据;

九、如何防止消息的重复消费

造成重复消费的原因,我认为有以下可能,生产者出现问题导致消息重发,使得broker收到多条一样的消息;或者是,消费者关闭自动提交且手动也不提交offset,并且重启了消费者,这样重启后的消费者就会重复消费之前没有提交offset的数据。所以解决办法有如下:

1、即使生产者消息重发有可能导致重复消费,为了防止消息丢失,不能将生产者消息重发功能关闭,可以通过在消费者端解决,所谓解决重复消费,也就是非幂等性的消费问题
(幂等性:多次访问的结果是一样的,例如,,查询-幂等、新增-非幂等、修改-幂等、删除-幂
等)
对于消息数据中的某个字段,例如订单id,利用数据库的唯一性约束(或者与原先的主键创建联合主键),防止非幂等性的操作;
使用分布式锁,以订单ID作为锁,保证只有一条记录可以创建成功。

2、消费者需要提交offset,因为自动提交容易丢失数据,所以推荐打开手动提交。

十、解决消息积压问题

消息积压问题出现的原因:
消费者的消费速度远远赶不上生产者生产消息的速度,导致Kafka中有大量的数据灭有被消费,随着没有被消费的数据堆积的越多,消费者寻址的性能会越来越差,最后导致Kafka对外提供的服务的性能越来越差,从而造成其他服务的访问速度也变慢,进而造成服务雪崩。

解决方案:
1、在消费者中,使用多线程,充分利用机器的性能进行消费;
2、创建多个消费组,多个消费者,部署到其他机器上,一起消费,提高消费者的消费速度。
3、创建一个消费者,该消费者在kafka中另外创建一个topic,配上多个partition,在配上多个消费者,该消费者将poll下来的消息不进行消费,而是直接转发到新的主题上,此时,新的书体的多个分区的多个消费者就可以开始一起消费了——不常用也不推荐,实在没有办法的办法。
4、通过业务的架构设计,提升业务层面的消费性能——也就是说优化业务代码,提高代码执行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值