kafka原理简介
Kafka是由LinkedIn开发的一个分布式的消息系统,使用Scala编写,它以可水平扩展和高吞吐率而被广泛使用。
目前越来越多的开源分布式处理系统如Cloudera、Apache Storm、Spark 都支持与Kafka集成。
kafka对消息保存时根据Topic进行归类,发送消息者就是Producer,消息接受者就是Consumer,将中间的存储阵列称作broker(代理),也称作一个kafka实例。
然后三者都通过ZooKeeper进行协调。
也即:
- 启动zookeeper的server
- 启动kafka的server
- Producer生产数据,然后通过zookeeper找到broker,再将数据push到broker进行保存
- Consumer通过zookeeper找到broker,然后再主动pull数据
kafka存储是基于硬盘存储的,然而却有着快速的读写效率,一个 67200rpm STAT RAID5 的阵列,线性读写速度是 300MB/sec,如果是随机读写,速度则是 50K/sec。
虽然内存读取速度明显快于硬盘读写速度,但是kafka却通过线性读写的方式实现快速地读写。
kafka各部分介绍
Producer | Topic | Partition
学习kafka一定要理解好Topic。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。
每个日志文件都是log entrie序列,每个log entrie代表一条消息,每条消息都有一个当前Partition下唯一的64字节offset,它指明了这条消息的起始位置。
Producer发送消息到broker时,会根据paritition机制选择将其存储到哪一个Partition。如果一个Topic对应一个文件,那这个文件所在机器的IO,将会成为这个Topic的性能瓶颈,有了Partition后,不同消息可以并行写入不同broker的不同partition里,极大提高了吞吐率。
解析topic
每条消息在partition中的位置称为offset(偏移量),类型为long型数字。消息即使被消费了,也不会被立即删除,而是根据broker里的设置,保存一定时间后再清除,比如log文件设置存储两天,则两天后,不管消息是否被消费,都清除。
Kafka保证一个Partition内的消息的有序性。
Broker
broker也即中间的存储队列。我们将消息的发布(publish)暂时称作 producer,将消息的订阅(subscribe)表述为consumer,将中间的存储阵列称作 broker(代理)。
Consumer
每个consumer属于一个consumer group。在kafka中,一个partition的消息只会被group中的一个consumer消费;可以认为一个group就是一个“订阅者”。一个Topic中的每个partition只会被一个“订阅者”中的一个consumer消费。
发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费。Kafka 消费端采用pull模式从broker拉消息
pull模式的优劣势
pull模式的优势:消费端自主控制消息的量,避免网络拥塞,因为消息的offset控制在消息端,还能简化broker的设计。服务端无状态,设计简单,不容易出错。
pull模式的缺点:不能以最快速度传递消息。
Zookeeper
kafka集群几乎不需要维护任何Consumer和Producer的信息。这些信息由Zookeeper保存。
保证送达(delivery guarantee)
At most once 消息可能会丢,但绝不会重复传输
At least once 消息绝不会丢,但可能会重复传输
Exactly once 每条消息肯定会被传输一次且仅传输一次
数据处理与commit的顺序,在很大程度上决定了消息从broker到consumer的delivery guarantee semantic。
at most once
如果读到消息就提交,则是at most once(至多一次),因为即使处理失败,因为消息已提交,offset已指向下一个,处理失败的消息也不会再处理了。
at least once
如果处理完成功后再提交,则是at least once(至少一次),消息必须处理成功。如果消息处理完,但commit时出错,这会导致重复消费消息,因此要求消息处理者要保证幂等。
Exactly once
业务需要做事务,保证 Exactly Once 语义
这里业务场景被区分为两个:
- 幂等操作
- 业务代码需要自身添加事务操作
幂等操作
所谓幂等操作就是重复执行不会产生问题,如果是这种场景下,你不需要额外做任何工作。但如果你的应用场景是不允许数据被重复执行的,那只能通过业务自身的逻辑代码来解决了。
事务保证
Kafka能支持分布式事务,保证微服务事务的完整性,关键是将偏移量和你要保存的状态通过事务保存到数据库,失败恢复时从这个偏移量开始从卡夫卡中重新读取,保证了消息和你的业务状态数据的一致性
有两种常用的方法在Kafka之上来获得恰好一次的语义:
1.将偏移量存储在与派生状态相同的DB中,并在事务中更新两者。重新启动时,从DB读取当前偏移量,然后从偏移位置开始读取卡夫卡。
2.以幂等的方式将状态更新和偏移量一起写入。例如,如果您的派生状态是一个key和一个跟踪出现次数的计数器,则将偏移量与计数值一起存储,并忽略任何偏移量<=当前存储值的任何更新。
Kafka 高可用
为了保证高可用,主要引入了Replication和Leader Election。
一个Kafka集群包括多个broker,这些broker在高可用环境下,可以对不同topic互为leader和follow。
Leader Election算法
常用的选择leader方法是少数服从多数(Majority Vote),它的优势是为了保证Leader Election能够进行,所容忍的fail的follower个数较少,否则就需要更多的Replica,因为需要大部分follower都同步有数据后,才能提交,这样的同步会加大latency(延迟),不适用于需要存储大量数据的系统使用。
Kafka使用了更像微软PacificA算法。
Kafka在ZooKeeper中动态维护了一个ISR,ISR里所有Replica都跟上了leader,只有ISR里的成员才能被选为Leader,在这种模式下,对于f+1个Replica,一个Partition能在保证不丢失已经commit的消息的前提下容忍f个Replica的失败。
Majority Vote相比ISR有不需等待最慢Broker这一优势,因为ISR需要等待所有机器都与Leader完成同步,而Majority Vote只需要最快的f+1个机器完成同步即可,但Majority Vote需要更多的机器(2f+1)才能容忍f个机器失败。
如果所有ISR都不work了,需要在可用性和一致性当中做出一个选择。
如果一定要等待ISR中的Replica“活”过来,那不可用时间会较长。也可选择第一个“活”过来的Replica作为Leader,由于这个Replica可能不是ISR中的Replica,因此它不保证已包含了所有已commit的消息,因此一致性会受影响。
如果所有replica都在zookeeper上注册观察者,会产生三个问题
Split-brain (脑裂),虽然ZooKeeper能保证所有Watch顺序触发,但不能保证同一时刻所有Replica看到的状态是一样的,会造成Replica的响应顺序不一致。
Herd effect(羊群效应) 如果宕机的那个Broker上的Partition比较多,会造成多个Watch被触发,造成集群内大量的调整。
ZooKeeper负载过重,每个Replica都来注册一上Watch,集群规模增加到几千个Partition时,ZooKeeper负载会过重
Kafka为了避免这样,在所有broker中选出一个controller,所有Partition的Leader选举都由controller决定,controller会将Leader的改变直接通过RPC通知需要为此作出响应的Broker,同时controller也负责增删Topic以及Replica的重新分配。
通信
Kafka的网络通信基于Java NIO开发,采用Reactor模式
1个Acceptor负责接受客户请求,N个Processor负责读写数据,M个Handler处理业务逻辑。
网友评论