Kafka架构原理

介绍

Kafka是一个分布式流式处理平台,具有三大主要功能:
1、发布和订阅消息
2、高度容错机制,消息持久化存储
3、实时消息处理
Kafka具有非常高的吞吐量,通常广泛应用于两大场景:
1、做为系统或应用程序间的数据通道,进行数据传输
2、做为应用程序来进行实时数据处理
Kafka提供了四大核心API:
1、Producer API。生产消息
2、Consumer API。消费消息
3、Stream API。流式处理
4、Connector API。连接多种数据源
Kafka架构原理_第1张图片

运行时架构

Kafka集群包括五大核心组件:
1、Broker。每台节点启动的Kafka实例
2、Zookeeper。Kakfa集群协调者,包括Leader选举和元数据存储
3、Producer。生产者负责将消息推送到Topic中
4、Consumer。消费者通常属于某一个消费者组
5、Topic。主题即为消息的拥有者,每个Topic有1-N个分区Partition
Kafka架构原理_第2张图片

Topic

Kafka以Topic来存储消息,每条消息都有一个Topic标签。生产者可以将消息写入不同的Topic,消费者按照自己需要订阅对应的Topic来获取消息。
通常Topic由一个或多个分区Partition,每个分区对应一个日志文件Log,每个Log包含多个Segment File。里面存储该分区的消息,消息在分区内是有序的。
Topic中分区数据存储方式如图:
Kafka架构原理_第3张图片
当生产者向Topic中写入一条消息时,该消息会以追加方式写入对应的日志文件尾部。每个分区接收的消息都会分配一个单调递增的序列号,通常我们称为offset,因此在分区内消息是有序的,但不是所有分区全局有序。同时在内存中会维护一个Segment File的offset列表,记录每一个文件offset的起始位置,方便进行定位查找。
Kafka架构原理_第4张图片

Producer

Producer生产者负责向Topic中写入消息数据,通常发送消息默认都是异步发送,即不会等待发送结果,不会阻塞线程。
众所周知消息是存储在分区中的,因此发送消息时如何确定该消息属于哪个分区呢?
通常在发消息时不指定key或key为null时,会默认触发Partitioner使用RoundRobin算法,将消息均匀分布在各个分区中。
发送消息时指定key,Partitioner会默认按照key进行hash计算,对分区数取模运算,判断消息属于哪个分区,然后将数据写入对应的分区中。因此相同key的消息会永远写入同一个分区中。
如果以上都不满足用户需求,用户可继承Partitioner接口,自定义Partitioner,按照自己逻辑决定消息该进入哪个分区,然后在发送消息时指定自定义的Partitioner即可。
Kakfa消息是持久化并提供一定可靠性的,有参数acks控制。
当acks=0时,表示客户端只管发送,不等待服务端确认消息是否写入成功。会造成消息丢失。
当acks=1时,表示等待服务端Leader成功收到消息并写入磁盘文件,如果在Leader同步副本时挂掉,导致副本没有正常同步,会造成消息丢失。
当acks=all或-1时,表示等待Leader和所有副本都写入成功,才会认为这条消息写入成功了。当然这种情况是最安全的,但是也会降低生产者的生产效率。虽然这种情况最安全,但是也不能保证消息不丢失。因为假设Partition只有一个Leader,没有其他副本,当Leader接受完消息后宕机,也会造成消息丢失。
因此在设置asks=all或-1之外,还要设置min.insync.replicas>=2。保证在ISR中至少有一个副本写入成功,这样就保证了一个Leader和一个副本数据写入成功。

副本机制

Kafka做为一个分布式平台,为了实现高容错,避免单点故障,采取了冗余副本机制。主副本和副本分散在各个节点,即使某个节点宕机,在其他节点也会有该节点数据副本,从而保证集群高度容错机制。当消费者访问的节点出现宕机情况时,会触发Rebalance,将请求转移到其他节点。
Kafka集群有Leader和Follower,Leader节点数据做为主文件,Follower节点数据做为副本,需要从Leader节点将数据同步过来。
Kafka如何实现副本同步?由于生产者会先将数据写入Leader节点,所以Leader节点肯定保存完整的数据。Leader节点维护一个ISR(In Sync Replicas)集合,里面存放着正在同步的副本信息。ISR中副本不能落后主副本太多,否则会被Leader从ISR集合中踢出,这些被踢出的副本叫OSR(Out Sync Replicas)。

消息模式

At Least Once:至少一次。如果生产者发送消息失败会进行重试,重试可能会导致消息重复写入,消费者重复消费。同时也可能会导致消息乱序。
如果服务端在成功写入日志文件后,正要给客户端发送ack时宕机,然后客户端重试发送消息,这样就会造成消息重复写入。
如果当第一次ack返回失败,客户端要进行重试的时候,第二条消息已经发出去了,这样就会导致分区内数据乱序。
delivery.timeout.ms控制客户端重试,表示发送消息到收到服务端成功或失败ack时间。这个值不能小于request.timeout.ms 和 linger.ms之和。
max.in.flight.requests.per.connection参数表示Producer端保存未得到响应的请求数量,设置为1可以保证消息顺序性
At Most Once:至多一次。当ack返回错误或超时,不会进行重试。当retries=0时,当消息没有成功写入,则会造成消息丢失。
Exactly Once:刚好一次。无论生产者是否重复发送消息,消费者也只会消费一次。此方式通过Kafka事务进行实现。
实现Exactly Once有两种方式:
方式一:幂等Producer方式,保证单个分区消息只会写入一次,不会出现重复消息。需要设置enable.idempotence=true,并且max.in.flight.requests.per.connection<=5,retries>0,acks ='all’或-1。
此方式缺点是单分区幂等性和单会话幂等性。单分区幂等性只能保证单个分区不会出现重复数据。单会话幂等性,Producer启动时会有一个PID,如果重启后Producer的PID发生变化,不能保证幂等性。
方式二:事务。Kafka提供了事务的API,保证消息原子性写入多个分区,要么全部成功,要么全部失败。启动事务,在Producer端设置transactional.id,enable.idempotence参数会自动设置为true,replication.factor>=3,min.insync.replicas=2。

Consumer

Kafka消费者不是单独存在的,它属于某个消费者组。每个消费者都有一个Group ID,相同Group ID的消费者位于同一个消费者组。
消费者有几种不同的消费方式,指定分区、不指定分区。Offset管理可由Zookeeper管理,也可消费者管理。当消费者管理offset时,需要手动提交offset。通常消费者管理offset时,会将消费进度(Offset)保存在外部存储中,例如Hbase。
Kafka中消费者有几个特点:
1、同一个消费者组的消费者采用合作模式进行消费,同一条消息只会被一个消费者消费
2、不同消费者组获取的消息是相同的
3、消费者不是以消息为单位进行消费,而是以分区为单位进行消费,每个分区在同一消费者组中只能有一个消费者
4、同一消费者组内,消费者数量不能大于分区数量

水平一般,能力有限,大数据小学生一枚。文章主要用于个人学习和总结,如果能给他人带来帮助,纯属意外。

你可能感兴趣的:(Kafka)