1、Kafka概览
Apache下的项目Kafka(卡夫卡)是一个分布式流处理平台,它的流行是因为卡夫卡系统的设计和操作简单,能充分利用磁盘的顺序读写特性。kafka每秒钟能有百万条消息的吞吐量,因此很适合实时的数据流处理。例如kafka在线日志收集系统可作为flume的实时消息sink端,再通过kafka的消费者将消息实时写入hbase数据库中。
卡夫卡以topic分类对记录进行存储,每个记录包含key-value和timestamp。
1.1、卡夫卡组件,角色简介
broker: 每个正在运行的kafka节点
producer:消息生产者
consumer:消息的消费者
consumer group:消费者组,同一个消费者组只能有一个consumer能消费其中一条消息
kafka server :也叫作broker, 已部署kafka的服务器, 以broker.id来区分不同的服务器
topic:主题, 主题中的每条消息包括key-value和timestamp。可以定义多个topic,每个topic又可以划分为多个分区,Kafka一般支持几十到几百个的topic,再多的话,性能就会有所下降
partition:topic下的消息分区,通过key取哈希后把消息映射分发到一个指定的分区,每个分区都映射到broker上的一个目录。一般每个分区存储在一个broker上
replica:副本, 每个分区按照生产者的消息达到顺序存放。每个分区副本都有一个leader(分区领导着)
leader replica:leader角色的分区副本,leader角色的分区处理消息的读写请求. Leader和follower位于不同的broker
follower replica:follower角色的分区副本,负责从Leader拉取数据到本地,实现分区副本的创建
zookeeper:严格来说这不是kafka的组件。但是在Kafka集群中, 很有必要通过Zookeeper管理kafka集群的配置、选举leader,以及在Consumer Group发生变化时进行rebalance。
下面说一下kafka的哪些组件需要注册到zookeeper——
为什么要注册到zk集群
- Kafka集群通过Zookeeper来管理kafka的配置,选举leader;
- 在Consumer Group发生变化时进行rebalance
- 所有的topic与broker的对应关系都由zk维护
kafka的哪些组件需要注册到zookeeper
(1)Broker注册到zk
每个broker启动时,都会注册到zk中,把自身的broker.id通知给zk。待zk创建此节点后,kafka会把这个broker的主机名和端口号记录到此节点。
(2)Topic注册到zk
当broker启动时,会到对应topic节点下注册自己的broker.id到对应分区的ISR列表中;当broker退出时,zk会自动更新其对应的topic分区的ISR列表,并决定是否需要做消费者的rebalance。
(3)Consumer注册到zk
一旦有新的消费者组注册到zk,zk会创建专用的节点来保存相关信息。如果zk发现消费者增加或减少,会自动触发消费者的负载均衡。
(注意,producer不注册到zk)
消息如何被消费的
- Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息;producer通过联系zk获取leader角色的消息分区码,把消息写到leader分区。
- Producer使用push模式将消息发布到broker;
- Consumer使用pull模式从broker订阅并消费消息;
1.2、分区副本机制
由于Producer和Consumer都只会与Leader角色的分区副本相连,所以kafka需要以集群的组织形式提供主题下的消息高可用。kafka支持主备复制,所以消息具备高可用和持久性。
一个分区可以有多个副本,这些副本保存在不同的broker上。每个分区的副本中都会有一个作为Leader(分区领导者)。当一个broker失败时,Leader在这台broker上的分区都会变得不可用,kafka会自动移除Leader,再其他副本中选一个作为新的Leader。
在通常情况下,增加分区可以提供kafka集群的吞吐量。然而,也应该意识到集群的总分区数或是单台服务器上的分区数过多,会增加不可用及延迟的风险。
1.3、创建副本的2种模式——同步复制和异步复制
Kafka动态维护了一个同步状态的副本的集合(a set of In-Sync Replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息只有被这个集合中的每个节点读取并追加到日志中,才会向外部通知说“这个消息已经被提交”。
只有当消息被所有的副本加入到日志中时,才算是“committed”,只有committed的消息才会发送给consumer,这样就不用担心一旦leader down掉了消息会丢失。
消息从leader复制到follower, 我们可以通过决定Producer是否等待消息被提交的通知(ack)来区分同步复制和异步复制。
同步复制流程:
1.producer联系zk识别leader
2.向leader发送消息
3.leadr收到消息写入到本地log
4.follower从leader pull消息
5.follower向本地写入log
6.follower向leader发送ack消息
7.leader收到所有follower的ack消息
8.leader向producer回传ack
异步复制流程:
和同步复制的区别在于,leader写入本地log之后,直接向client回传ack消息,不需要等待所有follower复制完成。
既然卡夫卡支持副本模式,那么其中一个Broker里的挂掉,一个新的leader就能通过ISR机制推选出来,继续处理读写请求。
1.4、判断一个broker节点是否存活
1.节点必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个节点的连接。
2. 如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久。Leader会追踪所有“同步中”的节点,一旦一个down掉了,或是卡住了,或是延时太久,leader就会把它移除。
1.5、卡夫卡高性能详解
那么Kafka到底是如何做到这么高的吞吐量和性能的呢?这篇文章我们来一点一点说一下。
1.5.1、页缓存技术 + 磁盘顺序写
首先Kafka每次接收到数据都会往磁盘上去写,如下图所示:
那么在这里我们不禁有一个疑问了,如果把数据基于磁盘来存储,频繁的往磁盘文件里写数据,这个性能会不会很差?大家肯定都觉得磁盘写性能是极差的。没错,要是真的跟上面那个图那么简单的话,那确实这个性能是比较差的。
但是实际上Kafka在这里有极为优秀和出色的设计,就是为了保证数据写入性能,首先Kafka是基于操作系统的页缓存来实现文件写入的。
操作系统本身有一层缓存,叫做page cache,是在内存里的缓存,我们也可以称之为os cache,意思就是操作系统自己管理的缓存。
在写入磁盘文件的时候,可以直接写入这个os cache里,也就是仅仅写入内存中,接下来由操作系统自己决定什么时候把os cache里的数据真的刷入磁盘文件中。仅仅这一个步骤,就可以将磁盘文件写性能提升很多了,因为其实这里相当于是在写内存,不是在写磁盘,大家看下图:
接着另外一个就是kafka写数据的时候,非常关键的一点,他是以磁盘顺序写的方式来写的。也就是说,仅仅将数据追加到文件的末尾,不是在文件的随机位置来修改数据。
普通的机械磁盘如果你要是随机写的话,确实性能极差,也就是随便找到文件的某个位置来写数据。但是如果你是追加文件末尾按照顺序的方式来写数据的话,那么这种磁盘顺序写的性能基本上可以跟写内存的性能本身也是差不多的。所以大家就知道了,上面那个图里,Kafka在写数据的时候,一方面基于了os层面的page cache来写数据,所以性能很高,本质就是在写内存罢了。
另外一个,他是采用磁盘顺序写的方式,所以即使数据刷入磁盘的时候,性能也是极高的,也跟写内存是差不多的。基于上面两点,kafka就实现了写入数据的超高性能。
那么大家想想,假如说kafka写入一条数据要耗费1毫秒的时间,那么是不是每秒就是可以写入1000条数据?但是假如kafka的性能极高,写入一条数据仅仅耗费0.01毫秒呢?那么每秒是不是就可以写入10万条数?
所以要保证每秒写入几万甚至几十万条数据的核心点,就是尽最大可能提升每条数据写入的性能,这样就可以在单位时间内写入更多的数据量,提升吞吐量。
1.5.2、零拷贝技术
从Kafka里我们经常要消费数据,那么消费的时候实际上就是要从kafka的磁盘文件里读取某条数据然后发送给下游的消费者,如下图所示。
那么这里如果频繁的从磁盘读数据然后发给消费者,性能瓶颈在哪里呢?假设要是kafka什么优化都不做,就是很简单的从磁盘读数据发送给下游的消费者,那么大概过程如下所示:
先看看要读的数据在不在os cache里,如果不在的话就从磁盘文件里读取数据后放入os cache。 接着从操作系统的os cache里拷贝数据到应用程序进程的缓存里,再从应用程序进程的缓存里拷贝数据到操作系统层面的Socket缓存里,最后从Socket缓存里提取数据后发送到网卡,最后发送出去给下游消费。
整个过程,如下图所示:
大家看上图,很明显可以看到有两次没必要的拷贝吧!
一次是从操作系统的cache里拷贝到应用进程的缓存里,接着又从应用程序缓存里拷贝回操作系统的Socket缓存里。
而且为了进行这两次拷贝,中间还发生了好几次上下文切换,一会儿是应用程序在执行,一会儿上下文切换到操作系统来执行。
所以这种方式来读取数据是比较消耗性能的。
Kafka为了解决这个问题,在读数据的时候是引入零拷贝技术。
也就是说,直接让操作系统的cache中的数据发送到网卡后传输给下游的消费者,中间跳过了两次拷贝数据的步骤,Socket缓存中仅仅会拷贝一个描述符过去,不会拷贝数据到Socket缓存。
大家看下图,体会一下这个精妙的过程:
通过零拷贝技术,就不需要把os cache里的数据拷贝到应用缓存,再从应用缓存拷贝到Socket缓存了,两次拷贝都省略了,所以叫做零拷贝。
对Socket缓存仅仅就是拷贝数据的描述符过去,然后数据就直接从os cache中发送到网卡上去了,这个过程大大的提升了数据消费时读取文件数据的性能。
而且大家会注意到,在从磁盘读数据的时候,会先看看os cache内存中是否有,如果有的话,其实读数据都是直接读内存的。
如果kafka集群经过良好的调优,大家会发现大量的数据都是直接写入os cache中,然后读数据的时候也是从os cache中读。相当于是Kafka完全基于内存提供数据的写和读了,所以这个整体性能会极其的高。
关于内存零拷贝文章介绍 https://my.oschina.net/u/3990817/blog/3045359
https://cloud.tencent.com/developer/article/1421266
1.5.3、批量传输 + GZIP等压缩数据
1.6、数据可靠性保证
声明:以上内容如果雷同,纯属抄袭。