kafka架构

kafka简介

Kafka是一个分布式系统,由服务器和客户端组成,通过高性能的TCP网络协议进行通信。它可以部署在裸机、虚拟机和内部环境中的容器上,也可以部署在云环境中。主要设计目标如下:
● 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间复杂度的访问性能(磁盘顺序写+零拷贝)
● 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条以上消息的传输(分区并发+批量发送)
● 支持Kafka Server间的消息分区,及分布式消费,同时保证每个Partition内的消息顺序传输(默认分区器保证相同key写到同一分区)
● 同时支持离线数据处理和实时数据处理
● Scale out:支持在线水平扩展

kafka架构

术语

● Broker
  Kafka集群包含一个或多个服务器,这种服务器被称为broker
● Topic
  每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
● Partition
  Parition是物理上的概念,每个Topic包含一个或多个Partition,每个分区对应服务器上的一个文件目录。
● Producer
  负责发布消息到Kafka broker
● Consumer
  消息消费者,向Kafka broker读取消息的客户端。
● Consumer Group
  每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group),每个Consumer group去消费不同的topic,而后consumer在group中去消费这个topic中的所有partition。由于同一个consumer group中一个consumer只能消费一个partition,因此一般情况下consumer个数不会超过分区个数。

拓扑结构

图1 kafka拓扑结构

图1 kafka拓扑结构图

如上图所示,一个kafka集群包括若干个producer,producer的来源可以是用户行为、服务日志等信息。若干broker构成集群,broker即是服务器,要求不会太高,便于集群的扩展。若干consumer group对日志进行消费。其中producer通过push的方式将消息发送到broker中,而consumer group则通过pull的方式拉取。在集群中还使用了zookeeper,用来做元数据的管理、broker管理以及controller选举(其中在kafka2.8之后可以在不依赖zookeeper的情况下运行kafka)。

Topic & Partition

Topic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。
以kafka默认topic: __consumer_offsets为例,默认分区为50,即存在50目录,编号从0-49,因此kafka分区命名规则为topicName-有序序号:
在这里插入图片描述

图2 '__consumer-offsets’分区图

当然上图在同一个broker中能看到所有的分区目录是因为这个topic默认factor是3。

副本分配算法如下:

  • 将所有N Broker和待分配的i个Partition排序。
  • 将第i个Partition分配到第(i mod n)个Broker上。
  • 将第i个Partition的第j个副本分配到第((i + j) mod n)个Broker上。

每个partition相当于一个巨型的文件被切分成了多个小文件,这个小文件的名称被称为segment,因此一个partition下有多个segment(默认大小1G),引入segment的目的是为了能够对文件快速删除,满足kafka日志根据时间大小等方式保留的目的。

  • segment file组成:由3大部分组成,分别为index file、timestamp index file和data file,index文件和data文件一一对应,成对出现,后缀”.index”和“.log”分别表示为segment索引文件、数据文件,而timestamp index文件则用于根据时间戳查找特定offset,如用于过期数据删除。
  • segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。
    在这里插入图片描述
    图3 partition中segment命名

producer的分区机制

KafkaProducer在调用send方法发送消息至broker的过程中,首先是经过拦截器Inteceptors处理,然后是经过序列化Serializer处理,之后就到了Partitions阶段,即分区分配计算阶段。在某些应用场景下,业务逻辑需要控制每条消息落到合适的分区中,有些情形下则只要根据默认的分配规则即可。
规则如下:

  • 是否指定了分区,如指定,则直接存储在指定分区;
  • 如未指定,则使用kafka提供的默认分区器DefaultPartitioner
    • 如果key为null,则按照一种轮询的方式来计算分区分配
    • 如果key不为null则使用称之为murmur的Hash算法(非加密型Hash函数,具备高运算性能及低碰撞率)来计算分区分配

当然也可以使用自定义分区器。

push vs pull

作为一个消息系统,Kafka遵循了传统的方式,选择由Producer向broker push消息并由Consumer从broker pull消息。一些logging-centric system,比如Facebook的Scribe和Cloudera的Flume,采用push模式。事实上,push模式和pull模式各有优劣。
  push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。push模式的目标是尽可能以最快速度传递消息,但是这样很容易造成Consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据Consumer的消费能力以适当的速率消费消息。
  对于Kafka而言,pull模式更合适。pull模式可简化broker的设计,Consumer可自主控制消费消息的速率,同时Consumer可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。

kafka消息传输保证

kafka在生产者和消费者之间的传输是如何保证的,我们可以知道有这么几种可能提供的delivery guarantee:

  • At most once 消息可能会丢,但绝不会重复传输
  • At least one 消息绝不会丢,但可能会重复传输
  • Exactly once 每条消息肯定会被传输一次且仅传输一次,很多时候这是用户所想要的。

在Producer向Kafka发送消息的时候,如果消息成功被写入到日志文件里面,消息就不会丢失了(多副本机制)。但是如果中间发生网络中断等异常,Producer没有接受到Ack消息,无法判断消息是否已经提交了,此时选择重复发送消息到Kafka,这样就会造成数据的重复写入(At Least Once)。因此在0.11.0.0版本中,Kafka引入了幂等性和事务性,以此来实现Exactly-Once语义。
所谓kafka的幂等性是指多接口调用产生的结果和只调用一次产生的结果是一致的。当然kafka的幂等性只能保证单个分区内写入的数据是不会重复的,这其中主要依赖与ProducerId和Sequence Number,Kafka内部会自动为每个Producer分配一个producer id(PID),broker端会为producer每个Partition维护一个<PID,Partition> -> sequence number映射。sequence number时从0开始单调递增的。

对于新接受到的消息,broker端会进行如下判断:

  • 如果新消息的sequence number正好是broker端维护的<PID,Partition> -> sequence number大1,说broker会接受处理这条消息。
  • 如果新消息的sequence number比broker端维护的sequence number要小,说明时重复消息,broker可以将其直接丢弃
  • 如果新消息的sequence number比broker端维护的sequence number要大过1,说明中间存在了丢数据的情况,那么会响应该情况,对应的Producer会抛出OutOfOrderSequenceException。
    开启幂等性是通过配置enable.idempotence(boolean类型,默认false)。

当然,为了解决幂等性无法跨分区的问题,kafka还引入了事务性,事务性是在<PID, Partition>->sequence number映射的前提下引入了transaction id,此处不作太多介绍,后续专门总结。

当然以上提及的事务性很难在consumer端保证,其中原因很多,首先,一个事务可能跨log segment,但由于kafka的消息保留策略,可能在事务进行中将该事务中过期的segment删除,导致后期消费时数据丢失;其次消费者可能不会同时从所有的参与事务的TopicPartitions分片中消费消息,也有可能导致数据丢失。当然还有其他的原因。

参考文献

kafka设计论文
kafka放弃zookeeper
官方文档
kafka集群partition分布原理
kafka存储那些事(版本较老,供参考)
kafka保证消息可靠性

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值