kafka第一讲:了解kafka

一、Kafka 是什么?

Kafka是一种高吞吐量的分布式发布-订阅消息系统。它最初由LinkedIn公司开发,之后成为Apache项目的一部分。Kafka是一个分布式的,可划分的,冗余备份的持久性的日志服务。Kafka 是用于构建实时数据管道和流应用程序。具有横向扩展,容错,wicked fast(变态快)等优点。

  • 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。
  • 高吞吐量 :即使是非常普通的硬件Kafka也可以支持每秒数百万 的消息。
  • 支持通过Kafka服务器和消费机集群来分区消息。
  • 支持Hadoop并行数据加载。

二、简单理解

举个例子,生产者消费者,生产者生产鸡蛋,消费者消费鸡蛋,生产者生产一个鸡蛋,消费者就消费一个鸡蛋,假设消费者消费鸡蛋的时候噎住了(系统宕机了),生产者还在生产鸡蛋,那新生产的鸡蛋就丢失了。

或者另一种情况:生产者很强劲(大交易量的情况),生产者 1 秒钟生产 100 个鸡蛋,消费者 1 秒钟只能吃 50 个鸡蛋,那要不了一会,消费者就吃不消了(消息堵塞,最终导致系统超时),消费者拒绝再吃了,「鸡蛋」又丢失了。

这个时候我们放个篮子在它们中间,生产出来的鸡蛋都放到篮子里,消费者去篮子里拿鸡蛋,这样鸡蛋就不会丢失了,都在篮子里,而这个篮子就是Kafka。

鸡蛋其实就是「数据流」,系统之间的交互都是通过「数据流」来传输的,也称为报文,也叫「消息」。消息队列满了,其实就是篮子满了,「鸡蛋」放不下了,那赶紧多放几个篮子,其实就是Kafka的扩容。

综上所述,Kafka 的概念就很好理解了,它就是那个「篮子」。

三、使用场景

通过简单形象的比喻,相信很多童鞋都大概了解Kafka是怎么一回事了。

那么我们要不要放「篮子」,以及什么情况我们需要去放这个「篮子」?

1.消息系统

对于一些常规的消息系统,Kafka是个不错的选择。

2.检测网站活性

Kafka可以作为「网站活性跟踪」的最佳工具。可以将网页/用户操作等信息发送到Kafka中。并实时监控,或者离线统计分析等。

3.日志系统

Kafka的特性决定它非常适合作为「日志收集中心」;

kafka是一个分布式消息队列。一般在架构设计中起到解耦、削峰、异步处理的作用。

四、重要概念

  • Producers:生产者,负责发布消息到Kafka broker。示例中:就是它来生产「鸡蛋」的。
  • Consumers:消费者,向Kafka broker读取消息的客户端。示例中:生出的「鸡蛋」它来消费。
  • Consumers Group:每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)
  • topic:每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)。示例中:你把它理解为标签,生产者每生产出来一个鸡蛋就贴上一个标签(topic),消费者可不是谁生产的「鸡蛋」都吃的,这样不同的生产者生产出来的「鸡蛋」,消费者就可以选择性的吃了。
  • broker:Kafka集群包含一个或多个服务器,这种服务器被称为broker。示例中:就是篮子的概念。
  • Partition是物理上的概念,每个Topic包含一个或多个Partition.

除了上面几个概念,还有一个也非常重要,那就是zookeeper。

1)Producer端使用zookeeper用来发现broker列表,以及和Topic下每个partition leader建立socket连接并发送消息。

2)Broker端使用zookeeper用来注册broker信息,已经监测partition leader存活性。

3)Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息。

五、Kafka 数据流

看完「鸡蛋」,「篮子」,我们逐渐清晰了,可zookeeper又出来搅合了,是不是有点懵比了?不要着急,看看下面这张图,也许你就了然于胸了。

 

Kafka的总体数据流是这样的:Producer往Broker里面的指定Topic中写消息,Consumers从Brokers里面拉去指定Topic的消息,然后进行业务处理。

图中有两个topic,topic 0有两个partition,topic 1有一个partition,三副本备份。可以看到consumer gourp 1中的consumer 2没有分到partition处理,这是有可能出现的。

那么,Kafka又是如何生产数据的?

 

 

 

 

 

如上图所示:

  • 首先创建一条记录,记录中一定要指定对应的topic和value,key和partition可选。
  • 先序列化,然后按照topic和partition,放进对应的发送队列中。
  • kafka produce都是批量请求,会积攒一批,然后一起发送,不是调send()就进行立刻进行网络发包。

如果partition没填,那么情况会是这样的:

1.填写了key:按照key进行哈希,相同key去一个partition。(如果扩展了partition的数量那么就不能保证了)

2.没填key:round-robin来选partition。

这些要发往同一个partition的请求按照配置,攒一波,然后由一个单独的线程一次性发过去。

六、设计原理

Kafka的设计初衷是希望作为一个统一的信息收集平台,能够实时的收集反馈信息,并需要能够支撑较大的数据量,且具备良好的容错能力。

1.持久性

kafka使用文件存储消息,这就直接决定kafka在性能上严重依赖文件系统的本身特性.且无论任何OS下,对文件系统本身的优化几乎没有可能。

文件缓存/直接内存映射等是常用的手段。

因为kafka是对日志文件进行append操作,因此磁盘检索的开支是较小的。同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数。

2.性能

需要考虑的影响性能点很多,除磁盘IO之外,我们还需要考虑网络IO,这直接关系到kafka的吞吐量问题。kafka并没有提供太多高超的技巧:

  • 对于producer端,可以将消息buffer起来,当消息的条数达到一定阀值时,批量发送给broker;
  • 对于consumer端也是一样,批量fetch多条消息,不过消息量的大小可以通过配置文件来指定;
  • 对于broker端,似乎有个sendfile系统调用可以潜在的提升网络IO的性能:将文件的数据映射到系统内存中,socket直接读取相应的内存区域即可,而无需进程再次 `copy` 和交换。

其实对于producer/consumer/broker三者而言,CPU的开支应该都不大,因此启用消息压缩机制是一个良好的策略。压缩需要消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。可以将任何在网络上传输的消息都经过压缩。

kafka支持gzip/snappy等多种压缩方式。

3.生产者

负载均衡:producer将会和Topic下所有partition leader保持socket连接。消息由producer直接通过socket发送到broker,中间不会经过任何路由层。

事实上,消息被路由到哪个partition上,有producer客户端决定。比如可以采用random、key-hash、轮询等,如果一个topic中有多个partitions,那么在producer端实现「消息均衡分发」是必要的。

4.消费者

consumer端向broker发送fetch请求,并告知其获取消息的offset。此后consumer将会获得一定条数的消息,consumer端也可以重置offset来重新消费消息。

5.消息传送机制

对于JMS实现,消息传输担保非常直接:有且只有一次(exactly once)。在kafka中稍有不同:

  • at most once: 最多一次,这个和JMS中 「非持久化」消息类似。发送一次,无论成败,将不会重发。
  • at least once: 消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功。
  • exactly once: 消息只会发送一次。
  • at most once: 消费者fetch消息,然后保存offset,然后处理消息。当client保存offset之后,但是在消息处理过程中出现了异常,导致部分消息未能继续处理。那么此后「未处理」的消息将不能被fetch到。
  • at least once: 消费者fetch消息,然后处理消息,然后保存offset。如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态。
  • exactly once:kafka中并没有严格的去实现(基于2阶段提交,事务),我们认为这种策略在kafka中是没有必要的。

通常情况下「at-least-once」是我们首选。(相比「at most once」而言,重复接收数据总比丢失数据要好)。

6.复制备份

kafka将每个partition数据复制到多个server上,任何一个partition有一个leader和多个follower(可以没有),备份的个数可以通过broker配置文件来设定。

  • leader处理所有的read-write请求,follower需要和leader保持同步。
  • follower和consumer一样,消费消息并保存在本地日志中;
  • leader负责跟踪所有的follower状态,如果follower落后太多或者失效,leader将会把它从replicas同步列表中删除。
  • 当所有的follower都将一条消息保存成功,此消息才被认为是committed,那么此时consumer才能消费它。- 即使只有一个replicas实例存活,仍然可以保证消息的正常发送和接收,只要zookeeper集群存活即可。(不同于其他分布式存储,比如hbase需要「多数派」存活才行)
  • 当leader失效时,需在followers中选取出新的leader,可能此时follower落后于leader,因此需要选择一个up-to-date的follower。
  • 选择follower时需要兼顾一个问题,就是新leader server上所已经承载的partition leader的个数,如果一个server上有过多的partition leader,意味着此server将承受着更多的IO压力。
  • 在选举新leader,需要考虑到负载均衡。

7.日志

日志文件中保存了一序列log entries(日志条目),每个log entry格式为「4个字节的数字N表示消息的长度」 + 「N个字节的消息内容」;

每个日志都有一个offset来唯一的标记一条消息,offset的值为 8 个字节的数字,表示此消息在此partition中所处的起始位置。

每个partition在物理存储层面,有多个log file组成(称为segment)。segmentfile的命名为 “最小offset”.kafka

8.分配

kafka使用zookeeper来存储一些meta信息,并使用了zookeeper watch机制来发现meta信息的变更并作出相应的动作(比如consumer失效,触发负载均衡等)

参考:https://toutiao.io/posts/6d4d9t

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章