简介前端
Kafka是基于发布订阅的消息系统。最初起源于LinkedIn,于2011年成为开源Apache项目,而后于2012年成为Apache顶级项目。Kafka用Scala和Java编写,因其分布式可扩展架构及可持久化、高吞吐率特征而被普遍使用。算法
消息队列缓存
一般在项目中,咱们会由于以下需求而引入消息队列模块:服务器
1.解耦:消息系统至关于在处理过程当中间插入了一个隐含的、基于数据的接口层。无需预先定义不一样的接口地址和请求应答规范,这容许数据上下游独立决定双方的处理过程,只须要约定数据格式便可任意扩展服务类型和业务需求。网络
2.缓冲:消息系统做为一个缓冲池,应对常见的访问量不均衡情形。好比特殊节假日的流量剧增和每日不一样时段的访问量差别。以及处理不一样数据类型所需的不一样实时性。使整个业务处理架构以较低成本得到必定灵活性。架构
3. 异步:不少时候,用户不想也不须要当即处理消息。消息队列提供了异步处理机制,容许用户把一个消息放入队列,但并不当即处理它。想向队列中放入多少消息就放多少,而后在须要的时候再去处理它们。app
Kafka的特色负载均衡
做为一种分布式的,基于发布/订阅的消息系统。Kafka的主要设计目标以下:异步
1.以时间复杂度为O(1)的方式提供消息持久化能力,即便对TB级以上数据也能保证常数时间复杂度的访问性能。分布式
2.高吞吐率。即便在很是廉价的商用机器上也能作到单机支持每秒100K条以上消息的传输。
3.支持Kafka Server间的消息分区,及分布式消费,同时保证每一个Partition内的消息顺序传输。
4.同时支持离线数据处理和实时数据处理。
5.支持在线水平扩展。
Kafka体系架构
如上图所示,一个典型的Kafka体系架构包括若干Producer(能够是服务器日志,业务数据,页面前端产生的page view等等),若干broker(Kafka支持水平扩展,通常broker数量越多,集群吞吐率越高),若干Consumer (Group),以及一个Zookeeper集群。Kafka经过Zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
名词解释:
Topic & Partition
一个topic能够认为一个一类消息,每一个topic将被分红多个partition,每一个partition在存储层面是append log文件。任何发布到此partition的消息都会被追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型的数字,它惟一标记一条消息。每条消息都被append到partition中,顺序写磁盘所以效率很是高。这是Kafka高吞吐率的重要基础。
Producer发送消息到broker时,会根据Paritition机制选择将其存储到哪个Partition。若是Partition机制设置合理,全部消息能够均匀分布到不一样的Partition里,这样就实现了负载均衡。若是一个Topic对应一个文件,那这个文件所在的机器I/O将会成为这个Topic的性能瓶颈,而有了Partition后,不一样的消息能够并行写入不一样broker的不一样Partition里,极大的提升了吞吐率。能够经过配置项num.partitions来指定新建Topic的默认Partition数量,也可在建立Topic时经过参数指定,同时也能够在Topic建立以后经过Kafka提供的工具修改。
Kafka的复制机制
Kafka 中的每一个主题分区都被复制了 n 次,其中的 n 是主题的复制因子(replication factor)。这容许 Kafka 在集群服务器发生故障时自动切换到这些副本,以便在出现故障时消息仍然可用。Kafka 的复制是以分区为粒度的,分区的预写日志被复制到 n 个服务器。 在 n 个副本中,一个副本做为 leader,其余副本成为 followers。顾名思义,producer 只能往 leader 分区上写数据(读也只能从 leader 分区上进行),followers 只按顺序从 leader 上复制日志。
日志复制算法(log replication algorithm)必须提供的基本保证是,若是它告诉客户端消息已被提交,而当前 leader 出现故障,新选出的 leader 也必须具备该消息。在出现故障时,Kafka 会从失去 leader 的 ISR 里面选择一个 follower 做为这个分区新的 leader ;换句话说,是由于这个 follower 是跟上 leader 写进度的。
每一个分区的 leader 会维护一个 ISR。当 producer 往 broker 发送消息,消息先写入到对应 leader 分区上,而后复制到这个分区的全部副本中。只有将消息成功复制到全部同步副本(ISR)后,这条消息才算被提交。因为消息复制延迟受到最慢同步副本的限制,所以快速检测慢副本并将其从 ISR 中删除很是重要。 Kafka 复制协议的细节会有些细微差异。
Kafka的同步机制
Kafka不是彻底同步,也不是彻底异步,而是一种ISR(In-Sync Replicas)机制:
1. leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR,每一个Partition都会有一个ISR,并且是由leader动态维护 。
2. 若是一个follower比一个leader落后太多,或者超过必定时间未发起数据复制请求,则leader将其从ISR中移除
3. 当ISR中全部Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。
Kafka提供了数据复制算法保证,若是leader发生故障或挂掉,一个新leader被选举并被接受客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader,或者说follower追赶leader数据。leader负责维护和跟踪ISR中全部follower滞后的状态。当producer发送一条消息到broker后,leader写入消息并复制到全部follower。消息提交以后才被成功复制到全部的同步副本。消息复制延迟受最慢的follower限制,重要的是快速检测慢副本,若是follower“落后”太多或者失效,leader将会把它从ISR中删除。
消息传输保障
前面已经介绍了Kafka如何进行有效的存储,以及了解了producer和consumer如何工做。接下来讨论的是Kafka如何确保消息在producer和consumer之间传输。有如下三种可能的传输保障(delivery guarantee):
At most once: 消息可能会丢,但毫不会重复传输
At least once:消息毫不会丢,但可能会重复传输
Exactly once:每条消息确定会被传输一次且仅传输一次
Kafka的消息传输保障机制很是直观。当producer向broker发送消息时,一旦这条消息被commit,因为副本机制(replication)的存在,它就不会丢失。可是若是producer发送数据给broker后,遇到的网络问题而形成通讯中断,那producer就没法判断该条消息是否已经提交(commit)。虽然Kafka没法肯定网络故障期间发生了什么,可是producer能够retry屡次,确保消息已经正确传输到broker中,因此目前Kafka实现的是at least once。consumer从broker中读取消息后,能够选择commit,该操做会在Zookeeper中存下该consumer在该partition下读取的消息的offset。该consumer下一次再读该partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit以后的开始位置相同。固然也能够将consumer设置为autocommit,即consumer一旦读取到数据当即自动commit。若是只讨论这一读取消息的过程,那Kafka是确保了exactly once, 可是若是因为前面producer与broker之间的某种缘由致使消息的重复,那么这里就是at least once。考虑这样一种状况,当consumer读完消息以后先commit再处理消息,在这种模式下,若是consumer在commit后还没来得及处理消息就crash了,下次从新开始工做后就没法读到刚刚已提交而未处理的消息,这就对应于at most once了。读完消息先处理再commit。这种模式下,若是处理完了消息在commit以前consumer crash了,下次从新开始工做时还会处理刚刚未commit的消息,实际上该消息已经被处理过了,这就对应于at least once。
要作到exactly once就须要引入消息去重机制。Kafka文档中说起GUID(Globally Unique Identifier)的概念,经过客户端生成算法获得每一个消息的unique id,同时可映射至broker上存储的地址,即经过GUID即可查询提取消息内容,也便于发送方的幂等性保证,须要在broker上提供此去重处理模块,目前版本尚不支持。针对GUID, 若是从客户端的角度去重,那么须要引入集中式缓存,必然会增长依赖复杂度,另外缓存的大小难以界定。不仅是Kafka, 相似RabbitMQ以及RocketMQ这类商业级中间件也只保障at least once, 且也没法从自身去进行消息去重。因此咱们建议业务方根据自身的业务特色进行去重,好比业务消息自己具有幂等性,或者借助Redis等其余产品进行去重处理。
Kafka做为消息队列:
传统的消息有两种模式:队列和发布订阅。 在队列模式中,消费者池从服务器读取消息(每一个消息只被其中一个读取); 发布订阅模式:消息广播给全部的消费者。这两种模式都有优缺点,队列的优势是容许多个消费者瓜分处理数据,这样能够扩展处理。可是,队列不像多个订阅者,一旦消息者进程读取后故障了,那么消息就丢了。而发布和订阅容许你广播数据到多个消费者,因为每一个订阅者都订阅了消息,因此没办法缩放处理。
kafka中的Consumer Group有两种形式:
a、队列:容许同名的消费者组成员共同处理。
b、发布订阅:广播消息给多个消费者组。
kafka的每一个topic都具备这两种模式。
传统的消息系统按顺序保存数据,若是多个消费者从队列消费,则服务器按存储的顺序发送消息,可是,尽管服务器按顺序发送,多个并行请求将会是异步的,所以消息可能乱序到达。这意味着只要消息存在并行消费的状况,顺序就没法保证。消息系统经常经过仅设1个消费者来解决这个问题,可是这意味着没用到并行处理。
kafka有比传统的消息系统更强的顺序保证。经过并行topic的parition,kafka提供了顺序保证和负载均衡。每一个partition仅由同一个消费者组中的一个消费者消费到。并确保消费者是该partition的惟一消费者,并按顺序消费数据。每一个topic有多个分区,则须要对多个消费者作负载均衡,但请注意,相同的消费者组中不能有比分区更多的消费者,不然多出的消费者一直处于空等待,不会收到消息。
Kafka做为存储系统
全部发布消息到消息队列和消费分离的系统,实际上都充当了一个临时存储系统。Kafka仍是一个很是高性能的存储系统。写入到kafka的数据将写到磁盘并复制到集群中保证容错性。并容许生产者等待消息应答,直到消息彻底写入。kafka的存储结构保证不管服务器上有50KB或50TB数据,执行效率是类似的,所以可达到水平扩展的目标。还能够认为kafka是一种专用于高性能,低延迟,提交日志存储,复制,和传播特殊用途的分布式文件系统。
Kafka流处理
Kafka的更高目标是实时流处理。在kafka中,流处理持续获取输入topic的数据,进行处理加工,而后写入输出topic。例如,一个零售APP,接收销售和出货的输入流,统计数量或调整价格后输出。
简单的需求能够直接使用producer和consumer API进行处理。对于复杂的转换,Kafka提供了更强大的Streams API。可构建聚合计算或链接流到一块儿的复杂应用程序。
综上所述,Kafka 的设计能够帮助咱们解决不少架构上的问题。可是想要用好 Kafka 的高性能、低耦合、高可靠性等特性,咱们须要很是了解 Kafka,以及咱们自身的业务需求,综合考虑应用场景。