kafka

【kafka】kafka 知多少?

2022-10-14  本文已影响0人  Bogon

Kafka体系架构=M个producer +N个broker +K个consumer+ZK集群

producer:生产者
Broker:服务代理节点,Kafka服务实例。
n个组成一个Kafka集群,通常一台机器部署一个Kafka实例,一个实例挂了其他实例仍可以使用,体现了高可用

consumer:消费者
消费topic 的消息, 一个topic 可以让若干个consumer消费,若干个consumer组成一个 consumer group
一条消息只能被consumer group 中一个consumer消费,若干个partition 被若干个consumer 同时消费,达到消费者高吞吐量

topic :主题
partition: 一个topic 可以拥有若干个partition(从 0 开始标识partition ),分布在不同的broker 上, 实现发布与订阅时负载均衡。
producer 通过自定义的规则将消息发送到对应topic 下某个partition,以offset标识一条消息在一个partition的唯一性。
一个partition拥有多个replica,提高容灾能力。
replica 包含两种类型:leader 副本、follower副本,
leader副本负责读写请求,follower 副本负责同步leader副本消息,通过副本选举实现故障转移。
partition在机器磁盘上以log 体现,采用顺序追加日志的方式添加新消息、实现高吞吐量

image.png

1,kafka的分区已经让读是从多个broker读从而负载均衡,不是MySQL的主从,压力都在主上;
2,kafka保存的数据和数据库的性质有实质的区别就是数据具有消费的概念,是流数据,kafka是消息队列,所以消费需要位移,而数据库是实体数据不存在这个概念,如果从kafka的follower读,消费端offset控制更复杂;
3,生产者来说,kafka可以通过配置来控制是否等待follower对消息确认的,如果从上面读,也需要所有的follower都确认了才可以回复生产者,造成性能下降,如果follower出问题了也不好处理

为什么kafka不支持主从分离?
https://www.zhihu.com/question/327925275/answer/705690755

假如A主题有4个分区,消费者组有2个实例,发布应用的时候,会先新启动一个服务节点加入消费组,通过重平衡分配到至少1个最多2个分区,消费者的偏移量是
1,重新从0开始
2,拿到分配分区的上一个消费者偏移量?
如果按照文章说的,即偏移量为0,消息应该会重复消费;
如果拿到上一个消费者的偏移量则不会消息重复消费,具体过程又是怎样的?

整个故事是这样的:假设C1消费P0,P1, C2消费P2,P3。如果C1从未提交,C1挂掉,C2开始消费P0,P1,发现没有对应提交位移,那么按照C2的auto.offset.reset值决定从那里消费,如果是earliest,从P0,P1的最小位移值(可能不是0)开始消费,如果是latest,从P0, P1的最新位移值(分区高水位值)开始消费。但如果C1之前提交了位移,那么C1挂掉之后C2从C1最新一次提交的位移值开始消费。
所谓的重复消费是指,C1消费了一部分数据,还没来得及提交这部分数据的位移就挂了。C2承接过来之后会重新消费这部分数据。

为什么 Kafka 不像 MySQL 那样允许追随者副本对外提供读服务?

因为MySQL一般部署在不同的机器上,一台机器读写会遇到瓶颈,Kafka中的领导者副本一般均匀分布在不同的broker中,已经起到了负载的作用。
即:同一个topic的已经通过分区的形式负载到不同的broker上了,读写的时候针对的领导者副本,但是量相比MySQL一个还实例少太多,个人觉得没有必要在提供度读服务了。(如果量大还可以使用更多的副本,让每一个副本本身都不太大)不知道这样理解对不对?

主写从读无非就是为了减轻leader节点的压力,而kafka中数据分布相对比较均匀,所说的Follower从节点,实际上也是其他topic partition的Leader节点,所以在Follower可以读数据,那么会影响Follower节点上的做为Leader的partition的读性能,所以整体性能并没有提升,但是带来了主从数据同步延迟导致的数据不一致的问题。

一个分区的问题,比如一个topic分为0,1,2 个分区,写入0到9条消息,按照轮询分布:
0分区:0,1,2,9
1分区:3,4,5,
2分区:6,7,8
那对于消费端来说,不管是p2p点对点模式,还是push/sub模式来说,如何保证消费端的读取顺序也是从0到9?
因为0到9条消息是分布在3个分区上的,同时消费者是主动轮训模式去读分区数据的,有没有可能读到后面写的数据呢?
比如先读到5在读到4?

目前Kafka的设计中多个分区的话无法保证全局的消息顺序。如果一定要实现全局的消息顺序,只能单分区。

1、 kafka是按照什么规则将消息划分到各个分区的?
2、既然同一个topic下的消息分布在不同的分区,那是什么机制将topic、partition、record关联或者说管理起来的?

  1. 如果producer指定了要发送的目标分区,消息自然是去到那个分区;否则就按照producer端参数partitioner.class指定的分区策略来定;如果你没有指定过partitioner.class,那么默认的规则是:看消息是否有key,如果有则计算key的murmur2哈希值%topic分区数;如果没有key,按照轮询的方式确定分区。

  2. 这个层级其实是逻辑概念。在物理上还是以日志段(log segment)文件的方式保存,日志段文件在内存中有对应的Java对象,里面关联了你说的这些。

1、主题中的每个分区都只会被组内的一个消费者实例消费,其他消费者实例不能消费它。
2、假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个 Failed 实例之前负责的分区转移给其他活着的消费者。
意思是1个分区只能同时被1个消费者消费,但是1个消费者能同时消费多个分区是吗?那1个消费者里面就会有多个消费者位移变量?
如果1个主题有2个分区,消费者组有3个消费者,那至少有1个消费者闲置?

在一个消费者组下,一个分区只能被一个消费者消费,但一个消费者可能被分配多个分区,因而在提交位移时也就能提交多个分区的位移。
针对你说的第二种情况,答案是:是的。有一个消费者将无法分配到任何分区,处于idle状态。

假如只有一个Producer进程,Kafka只有一分区。
Producer按照1,2,3,4,5的顺序发送消息,Kafka这个唯一分区收到消息一定是1,2,3,4,5么?
Producer端,网络,数据格式等因素,会不会导致Kafka只有一个分区接收到数据顺序跟Producer发送数据顺序不一致?

如果retries>0并且max.in.flight.requests.per.connection>1有可能出现消息乱序的情况 。

重平衡:Rebalance。
消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。
Rebalance 是 Kafka 消费者端实现高可用的重要手段。

Kafka可以关闭重平衡吗?
可不可在逻辑上新建一个消费者或者将failed消费者重启 而不是分配给其他消费者?

使用standalone consumer就完全避免rebalance了。
事实上很多主流大数据流处理框架(Spark、Flink)都是这么使用的。

一个分区的N个副本是在同个Broker中的吗,还是在不同的Broker中,还是说是随机的?
一个分区的N个副本一定在N个不同的Broker上。

如果consumer一直不提交位移,会有什么影响?
目前想到的是:当前consumer 实例宕机,后续消费该分区的消费者实例就只能遵从auto.reset.offset的指定了。除此之外还有其他问题吗?

确实就像你说的,没有提交位移的数据,自然也就无法获取之前消费进度。其他没有什么影响了。

分区数量一开始定义后,后面可以增加分区后,原来分区的数据应该不会迁移吧?分区数量可以减少吗?

不会自动迁移,需要你手动迁移。分区数不可以减少。

MySQL 中从追随者读取数据对server和client都没有影响,而Kafka中从追随者读取消息意味着消费了数据,需要标记该数据被
消费了,涉及到做一些进度维护的操作,多个消费实例做这些操作复杂性比较高,如果可以从追随者读也可能会牺牲性能。

我个人认为维护成本不高。
Kafka中消费进度由clients端来操作,即消费者来决定什么时候提交位移,而且是提交到专属的topic上,与副本本身关联不大。
实际上社区最近正在讨论是否允许follower副本提供读服务。
不过我同意的是,follower副本提供读服务后会推高follower所在broker的磁盘读IO
Kafka不采用主从分离的讨论最近火起来了。
如果要让follower抗读,需要解决很多一致性的问题,另外Kafka也不属于典型的读多写少场景,主从分离的优势不明显。

high water mark怎么理解?

在Kafka中你可以认为一个分区的HW表征了当前该分区下所有副本都已经保存了的消息的最大位移。

在Kafka官网看的,”Each partition has one server which acts as the "leader" and zero or more servers which act as "followers". 请问这里的server该作何理解?

server = Broker

1、客户端会首先请求topic分区的leader副本在哪个broker上,内部自动执行的,你无需操心; 只是特别好奇他是怎么选的? 因为每个topic的每个分区leader还不一样。
2、重平衡时每个消费者都会尽力去做一次位移提交(如果它会提交位移的话),这样当rebalance完成后Kafka会告诉它们订阅分区当前消费到了那里。
我理解你说的应该是在检测到有节点挂了,kafka会进行重平衡,此时未挂的节点会尽力提交自己的位移,对吧?但是针对挂了的节点我理解是没法位移提交的,针对没有没有位移提交怎么处理呀?

  1. 客户端发送Metadata请求获取每个topic分区的leader,之后再发送真实的数据请求(Produce请求或Fetch请求)
  2. 如果消费者挂了自然是没法提交位移,所以有重复消费的问题。Kafka一直是承认这一点的啊,即消费者端存在消息被消费多次的情况

kafka能否做到多个消费者消费一个生产者生产的数据,并能保证每个消费者消费的消息不会重复,做到并行消费?

Kafka提供了消费者组实现你说的这个需求。

1.kafka支持接入多少生产者,生产者连接数有没有限制?
2.kafka生产侧是否支持f5负载均衡,或者是说如果kafka的服务ip发生变化,有没有什么设计,可以避免让生产者修改broker的列表,dns?

  1. 连接数在Kafka端没有限制
  2. Kafka 2.1开始支持客户端的DNS解析,方法是设置客户端的client.dns.lookup

client.dns.lookup
控制客户端如何使用DNS查找,默认为use_all_dns_ips,如果设置为use_all_dns_ips,那么当查找返回一个主机名的多个IP地址时,就会在连接失败之前尝试连接它们,对引导和广告服务器都适用。
如果值是resolve_canonical_bootstrap_servers_only,则每个条目将解析并展开为规范名称列表。
取值范围default, use_all_dns_ips, resolve_canonical_bootstrap_servers_only。

消费者位移可以手动往回调么,当位移向前后,分区里之前的数据还会存在么,如果存在啥时会被删除呢?

数据通常都在呢。消息何时在Broker端被删除取决于Broker端的配置。
通常情况下,Kafka默认保存最近一周的数据,一周前的数据自动删除。

kafka采用多个多个分区支持负载均衡,这样无法保证全局顺序唯一吧,现在业内常采用单分区来解决这个问题,但是这样又丢失了负载均衡能力,后面kafka会针对这个问题会进一步加强吗?

没有。不光Kafka,所有partitioned database都有这个问题。

同一主题下的分区有没有可能到不同的borker上?
同一分区的副本有没有可能在不同的borker上

“同一主题下的分区有没有可能到不同的borker上?” ——非常可能,而且也是期望的结果。
“同一分区的副本有没有可能在不同的borker上” —— 必须如此。同一分区的不同副本必然在不同的broker上。

leader 副本分布在哪个broker上随机的吗?还是有什么机制
假如broker1挂掉,broker2上的follower副本会变为leader副本吗?
假如不止一个follower副本,是不是有某种选举方式来决定哪个follower副本会升级为leader副本?

  1. 有算法,不是随机的。其实不只是leader,整个副本在broker上的分配策略大体上都遵循轮询的公平法则。
  2. 从follower中选择leader的算法如下:
    2.1 从ISR中选择存活的第一个副本为新leader
    2.2 如果ISR为空,看是否开启了unclean leader选举,如果没有开启,那么Kafka干脆就不选leader了,直接将分区置于不可用状态;否则Kafka就从剩下的存活副本中选第一个副本作为leader(这里的顺序就是ZooKeeper中保存的副本集合顺序,即assigned_replicas项)

每个消费者有着自己的消费者位移。“重平衡”时Kafka怎么知道已挂的消费者消费到哪里了?

重平衡时每个消费者都会尽力去做一次位移提交(如果它会提交位移的话),这样当rebalance完成后Kafka会告诉它们订阅分区当前消费到了那里。

对于发布/订阅者模式来说,消息往往是存储在某种形式的队列中的,那如何把这个队列中的消息推送给消费者呢?
我们公司的做法就是利用一个线程去轮询将消息分发到各自的消费者中;
可是,从实际业务场景来看,往往消息生产者只在某个特定的时间段去生产消息,而且消息是少量的,几十到几百个;服务器一直开一个线程去轮询我总觉得有些浪费资源;请问除了轮询以外有没有其他办法?

目前看很难。Kafka不能推送消息给consumer。Consumer只能不断地轮训去获取消息。从Kafka流向consumer的唯一方式就是通过poll。另外维持一个长连接去轮训的开销通常也没有你想的那么大,特别是Kafka用的是Linux上的epoll,性能还不错,至少比select好。

有多个broker才会有副本,一个broker里的topic进行分区是没有意义?

也是有意义的。通常情况下单broker下多分区(不好太多)也要比单broker下单分区的TPS来的大。
broker部署和topic,partition并无直接关联。
不过通常情况下一个broker上的分区数不要超过2000。
Kafka的副本拉取是完全异步的。另外实际上最新版本已经不单纯依赖高水位来判断了,而是依靠leader epoch。

一个Broker就是一个Kafka实例吗?在Kafka集群中,一个Kafka实例可以有多个Broker吗?
一个Topic可以由多个Broker处理吗?topic下面不同的分区的数据可以由不同的Broker来处理吗?
消费者组去消费消息的时候,不同的消费者可以同时消费同一个分区里面的消息吗?

  1. 一般来说,一个Broker就是一个Kafka服务器实例。集群中允许有多个Broker。实际上绝大多数情况下都是多实例的集群模式。
  2. 向Topic生产消息本来就涉及到多个Broker。不同分区的数据可以由不同的Broker来处理。
  3. 如果属于同一个消费者组,这些消费者不能同时消费同一个分区里面的消息。

为什么 Kafka 不像 MySQL 那样允许追随者副本对外提供读服务?

  1. 首先Partition已经承担了Master-Slave的“高性能”功效。
  2. Follower的设计和存在,是为了“高可用”的目的,所以“高性能”的重担不在它的肩上。
  3. Leader是source of truth,所以读写都应该在leader上。
  4. 由于有__consumer_offsets的机制,每次读写都应该是在leader上,如果replica也参与了read服务,那么consumer offet就乱了。

MySQL其实主从节点之间也会存在主从延迟,数据不一致的问题,针对一些要求数据强实时性,强一致性的业务来说,最好不要使用读写分离的方案。
数据库中存在数据历史版本的概念,而kafka中的是流数据的概念。

数据一致性的问题,如果允许从follower节点读取数据,那么因为主从延迟使得无法快速获取数据,另外还会导致即使在一个partition中的数据也无法保证顺序性。

kafka如果提供副本的读,可能会导致消息被重复消费,不好保证消费者的幂等性。
MySQL不一样,读的内容是完全可以重复并且不需要幂等性保障。
从follower读主要的麻烦在于滞后性,因为follower通常落后于leader,所以有可能读不到消息。

我倒觉得Kafka和MySQL都是采用基于日志(log-based)的结构来做的同步,本质上都属于CDC(capture data change)。MySQL也有主从不一致的问题。

目前最新版本支持有限度的follower对外提供读服务

点对点的实现方式通过消费组,但是如果多个消费组订阅了同一个 topic ,是不是同一个消息依然会被多个消费组的消费者去消费?

是的。消费者组之间没有关联

怎么使用自带带shell脚本获取到某个topic的当前最新的offset位置呀?

bin/kafka-run-class.sh kafka.tools.GetOffsetShell

生产者生产的每条消息只会被发送到一个分区中,那其他没有使用的分区有什么用?
分区不可用,这样的情况下,是不是分区上所有的主题都不能读写了?
多个消费者可以消费同一个主题,那消费者自己的offset,是不是保存在client端?

第一,我们不可能只选择往一个分区发送消息吧。
第二,分区是在主题之下的,分区不可用了表示clients无法访问这个主题下的分区了。
第三,offset保存在broker端的内部topic中,不是在clients中保存。

关于replica的leader和follower之间如何复制数据保证消息的持久化的问题,我了解的是有3种模式:
1.生产者消息发过来以后,写leader成功后即告知生产者成功,然后异步的将消息同步给其他follower,这种方式效率最高,但可能丢数据;
2.同步等待所有follower都复制成功后通知生产者消息发送成功,这样不会丢数据,但效率不高;
3.折中的办法,同步等待部分follower复制成功,如1个follower复制成功再返回,这样兼顾效率和消息的持久化。
具体选择哪种要根据业务情况进行选择。

目前Kafka不支持第三种“折中”办法。。。要么只写leader,要么所有follower全部同步。
但是,我同意很多分布式系统是可以配置同步follower和异步follower共存的,比如一个同步follower+N-1个异步follower的伪同步。
Facebook的MySQL就是这个原理,参见http://yoshinorimatsunobu.blogspot.com/2014/04/semi-synchronous-replication-at-facebook.html

至于追随者副本,它只做一件事:向领导者副本发送请求,请求领导者把最新生产的消息发给它,这样它能保持与领导者的同步...kafka 不是领导者副本向追随者副本 推送消息吗? 这里说的是追随者副本向领导者副本拉取消息?

Kafka不是PUSH模型,而是PULL模型,即follower副本不断地从leader处拉取消息。
follower副本会不停地同步leader副本的消息,当leader副本挂掉后,follower副本就派上用场了,Kafka从ISR的follower副本中选择一个出来当新的leader

如果允许follower节点提供读,应该就可以省掉partition这个概念,设计上岂不是更简单?

partition的作用在于分区,将海量的消息划分到不同的broker上进行处理,至于分区下的副本主要目的在于备份,两者的目的不是一样的。

如果消息中包含时间,而消费端消费和这个时间强相关,要是消费不过来,岂不是接收到的数据都会被认为是已经过时的不正
确的数据,怎么解决这个问题?

你的意思是特定时间段的消息必须要在某个时间点前消费完否则就失效了,是吗?
如果是这样的话只能加速您的consumer消费速度了。
事实上,流处理中的窗口化就是类似的想法

一个topic内所能够承载的分区数目有没有一个上限?
如果没有,那么分区的数量在多少是比较合适的,才不会对性能有较大的影响呢?

没有上限。也没有放之四海而皆准的标准推荐数量,只能根据自身的硬件条件逐渐测试。

如果producer指定了要发送的目标分区,消息自然是去到那个分区?
这里的producer是怎么指定的?因为我这边有三个分区,只用到了第一个分区。

首先创建一个类实现org.apache.kafka.clients.producer.Partitioner接口,然后指定producer的partitioner.class属性为该类的地址即可实现你自己的分区逻辑。

“重平衡”,是不是就是再分配,kakfa中应该有再分配的这个概念,这两个是不是同一个?

个人觉得这是两个概念。
我倾向于认为rebalance是重平衡,reassignment是再分配。
这两个在Kafka中是不同的操作。

如何可以清空一個已有的topic中的所有舊的record,除了刪除topic重新創建之外有沒有其他方式?

可以删除topic中的记录,不过是以offset的维度。
所以你要先定义哪些offset之前的消息是旧消息

1.假如我向分区中发了2条消息,如果第一条消息确认被消费掉了,那么第二条消息的offset还是1吗,会变成0吗?
2.如果问题1里的offset不变,那么消费者位移直接用offset就可以了吧,反正该消息在分区内唯一又不会变,为什么要多整出个消费者位移的概念?

  1. 消息一旦被写入到分区日志中位移就不会变化了
  2. 消费者位移是表示消费者进度的。每个消费者都有自己的进度,因此引入了消费者位移

如果一个broker主挂了,那么他的消费者位移,如何传递给从?

内部位移topic __consumer_offset 的写入都是acks=all的,所以follower副本与leader副本实时同步。

上一篇下一篇

猜你喜欢

热点阅读