kafka知识总结

285 阅读10分钟

kafka知识总结

一、前言

公司统一使用了kafka作为消息中间件,自己为了方面各个业务的使用,统一封装了组件给他们使用,借此多了解kafka,积累知识。

二、 kafka简介

2.1 简介

在kafka官网的首页,有一行这样的描述:

Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.

翻译过来就是: Apache Kafka 是一个开源的分布式事件流平台,被成千上万的公司用于高性能数据管道、流分析、数据集成任务关键型应用程序。

核心特性:

image.png

  • 高吞吐量
  • 可扩展的
  • 永久存储
  • 高可用 记住上面的核心特性,在后面的阅读中带着疑问去思考为什么kafka会说上述是他的核心特性。

2.2应用场景

  • 日志聚合: 许多人使用 Kafka 作为日志聚合解决方案的替代品。日志聚合通常从服务器收集物理日志文件,并将它们放在一个中心位置(可能是文件服务器或 HDFS)进行处理。Kafka 抽象出文件的细节,并将日志或事件数据更清晰地抽象为消息流。这允许更低延迟的处理和更容易支持多个数据源和分布式数据消费。与 Scribe 或 Flume 等以日志为中心的系统相比,Kafka 提供同样出色的性能、由于复制而产生的更强大的持久性保证以及更低的端到端延迟。
  • 流处理: 许多 Kafka 用户在由多个阶段组成的处理管道中处理数据,其中原始输入数据从 Kafka 主题中消费,然后聚合、丰富或以其他方式转换为新主题以供进一步消费或后续处理。例如,用于推荐新闻文章的处理管道可能会从 RSS 提要中抓取文章内容并将其发布到“文章”主题;进一步的处理可能会对该内容进行规范化或去重,并将清理后的文章内容发布到新主题;最终处理阶段可能会尝试向用户推荐此内容。此类处理管道基于各个主题创建实时数据流图。
  • 事件溯源 事件溯源是一种应用程序设计风格,其中状态更改被记录为按时间排序的记录序列。Kafka 对非常大的存储日志数据的支持使其成为以这种风格构建的应用程序的出色后端。
  • 提交日志 Kafka 可以作为分布式系统的一种外部提交日志。该日志有助于在节点之间复制数据,并充当故障节点恢复其数据的重新同步机制。Kafka 中的日志压缩功能有助于支持这种用法
  • 用户活动跟踪: Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。

三、基本概念

image.png

  • Producer: 生产者,就是发送消息的一方。负责创建消息,然后将其发送到Kafka服务器。
  • Consumer 消费者,就是接受消息的乙方。消费者链接到Kafka上并接受消息,进而进行相应的业务逻辑处理。
  • Consummer Group 消费者组。一个消费者组可以包含一个或者多个消费者。使用多partition+多consumer的消费方式可以极大提高数据下游的处理速度,同一个消费组中的消费者不会重复消费消息。同样的,不同消费组中的消费者消费消息时互不影响。Kafka就是通过消费组的方式实现P2P模式和广播模式。
  • Broker: 服务代理节点,Broker是Kafka服务节点,即Kafka的服务器。
  • Topic: Kafka中的消息以Topic为单位进行划分,生产者将消息发送到特定的Topic,而消费者负责订阅Topic中的消息进行消费。
  • Partition Topic是一个逻辑的概念,它可以细分为多个分区,每个分区只属于单个主题,同一个主题下不同分区包含的消息是不同的,分区在存储层面可以看作一个可追加的日志文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。
  • Offset offset是消息在分区中的唯一标识,Kafka通过它来保证消息在分区内的顺序性,同一个topic不同分区的offset是不同的,所以无法保证topic的有序性。
  • Replication 副本,是Kafka保证数据高可用的方式,Kafka同一个Partition的数据可以在多个Broker上存在多个副本,通常只有主副本才对外提供读写服务,当主副本所在broker崩溃或发生网络异常,则会重新选举leader
  • Record 实际写入kafka中并可以被读取的消息记录。每个record中包含了key、value和timestamp。
  • Zookeeper zookeper即kafka居群元数据管理系统,由于kafka是一个分布式消息系统,出于分布式的原因,kafka系统需要zookeeper来协调管理服务。其主要的作用就是选举主题分区Leader、协调各个代理节点服务、存储kafka元数据信息等。

image.png

四、服务机制

image.png 同样的,结合上面kafka的消息机制图,讲解从消息发送到消费涉及的全部过程。

4.1 生产与同步

  • 消息发送 producer生产消息后,首先发送Metadata请求到任意一个broker节点,获取到topic的leader分区信息(broker地址端口等),然后直接将消息发送到对应的broker当中。消息压缩:Producer端可以通过GZIP或Snappy格式对消息集合进行压缩。Producer端进行压缩之后,在Consumer端需进行解压。压缩的好处就是减少传输的数据量,减轻对网络传输的压力,在对大数据处理上,瓶颈往往体现在网络上而不是CPU(压缩和解压会耗掉部分CPU资源)
  • 分区均衡 partition的数量保持和broker一致,实现高可用。发送消息一般有三种方式,一种是直接指定topic发送,broker会随机发送到partition(每个版本不同,有的是轮询,有的是随机,有的是一定时间只发送到一个分区),一种是指定分区,还有一种是指定key(对key哈希和partition数量取模,可以自定义实现哈希)。
  • leader选举 image.png 图片来源

如上图所示,当Broker0宕机之后,P1 Leader也变成不可用状态,此时需要在P1 Replica1和P2 Replica2当中选出新的leader,如果这两个副本都在ISR队列当中,则随机选取一个作为新的leader,如果不在,则取OSR队列中选取(会面临丢失消息的风险)。

  1. ISR队列(In Sync Replicas) 每当leader收到生产者的消息之后,会将消息同步给其他副本,如果这些副本拉去的同步数据相对于leader 在一个可接受范围的延时中(可以配置),则会将这些副本放入ISR队列。

  2. OSR队列(Out Sync Replicas) 相对于ISR队列,如果副本拉去的同步数据不在可接受的延时中,则放入OSR队列,认为是同步有问题的数据。当然,后续如果同步数据跟上来了,会移到ISR队列。

  3. AR 所有的副本数成为AR(Assigned Replicas),即AR = ISR + OSR

而选举的事情由谁负责呢?broker当中也会有一个controller,它除了一般的存储消息,还会管理partition的分配,broker故障,topic创建等,详情可以查阅该文章Kafka的Controller Broker。值得一提的是,kafka通过zookeeper的分布式锁和epoch来防止脑裂的发生。

4.2 存储机制

对于生产者和发送者来说,partition就是一个存储的基本单位,但实际上,partition在broker当中是每个segment组成的,而每个segment文件又包含.index文件、.log文件、.timeindex文件(早期版本中没有)三个文件。如下图所示:图片来源

image.png

image.png

  1. log log文件存储了真正的message,它的文件名以上一个最后的offset数值命名,数值最大为64位long大小,19位数字字符长度,没有数字用0填充。

  2. index index文件以稀疏哈希的方式,offset映射log文件中真正message的物理偏移地址,从而方便消费者快速定位到message位置。

  3. timestamp 与index不同的是,通过时间戳映射log文件中真正message的物理偏移地址,从而方面消费者消费指定时间的消息。

  • 消息消费 消费者以消费组为单位,向broker主动拉取消息的过程,通过zookeeper(早期版本)或者broker(leader分区所在)记录的offset位置。在zookeeper中,offset以/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id] --> offset_counter_value的结构分开保存,但是由于每次更新都只能单次影响性能,所以后续的版本中,采用broker存储offset的方式。 在broker中,offset默认存储在以下的50个目录,offset的存放位置决定于groupId的hash值取模。
__consumer_offsets-0            __consumer_offsets-22           __consumer_offsets-36           __consumer_offsets-5
__consumer_offsets-1            __consumer_offsets-23           __consumer_offsets-37           __consumer_offsets-6
...

每次消费完成之后会更新最新的offset,方面下一次消费为最新的位置。

  • 如何保证消息的可靠性 保证消息可靠性的前提是,生产者发布的message不会丢失,消费者消费的message不会丢失(或者不希望重复,重复有时候对业务来说是错误的)。 生产者发送完message之后,有三种方式确认该消息发送完毕
  1. 无需确认,发送完成之后,就任务message发送成功了。
  2. broker确认收到后为成功。
  3. broker收到后,且所有的副本都同步完成,才为成功(高度一致性)。 使用者需考虑自身业务情况选择配置合适的方式,一般的配置为第二种。

消费者消费message之后,由于存在多消费组多线程的情况,消费的速度并不一致,所以回导致提交的offset不连续,而broker总是记录最后提交的offset,所以会有重复消费和丢失消费的情况。 具体而言,重复消费会在以下的场景中出现:

1)消费者宕机、重启等。导致消息已经消费但是没有提交offset。

2)消费者使用自动提交offset,但当还没有提交的时候,有新的消费者加入或者移除,发生了rebalance。再次消费的时候,消费者会根据提交的偏移量来,于是重复消费了数据。

3)消息处理耗时,或者消费者拉取的消息量太多,处理耗时,超过了max.poll.interval.ms的配置时间,导致认为当前消费者已经死掉,触发再均衡。

总结:尽量使用自动提交,根据消费处理速度设置max.poll.interval.ms时间。幂等可以参考这篇文章zhuanlan.zhihu.com/p/141720058

以及多线程消费——www.jianshu.com/p/0a0563397…

消费丢失会在以下的场景中出现:

生产者发送消息没有开启所有副本同步完成之后确认,会在broker宕机之后丢失消息,消费者使用自动提交,但是消费失败也会导致偏移量被提交以至丢失消息。

解决方式: 由于生产者开启所有副本同步完成之后确认会导致吞吐量降低,所以还是启动broker确认收到为发送成功的信号,吞吐量较高,此时生产者发送消息之后要有回调通知的send方式,且设置一定的重试时间和重试间隔。消费者设置手动提交,控制消费线程。