Kafka学习总结(二)——Kafka设计原理

来源:互联网 发布:js调用电脑摄像头拍照 编辑:程序博客网 时间:2024/06/06 01:04

2.1、kafka的场景和架构

在我们大量使用分布式数据库、分布式计算集群的时候,是否会遇到这样的一些问题:

  • 我想分析一下用户行为pageviews,以便我能设计出更好的广告位

  • 我想对用户的搜索关键词进行统计,分析出当前的流行趋势。这个很有意思,在经济学上有个长裙理论,就是说,如果长裙的销量高了,说明经济不景气了,因为姑娘们没钱买各种丝袜了。

有些数据,我觉得存数据库浪费,直接存硬盘又怕到时候操作效率低。
kafka在实际应用中被大量的用于日志系统,首先我们要明白什么是消息系统,在kafka官网上对kafka的定义叫:A distributed publish-subscribe messaging system(一个分布式发布-订阅消息传递系统)。publish-subscribe是发布和订阅的意思,所以更准确的说kafka是一个消息订阅和发布的系统。publish- subscribe这个概念很重要,因为kafka的设计理念就可以从这里说起。
这里写图片描述

多个broker协同合作,producer和consumer部署在各个业务逻辑中被频繁的调用,三者通过zookeeper管理协调请求和转发。这样一个高性能的分布式消息发布与订阅系统就完成了。图上有个细节需要注意,producer到broker的过程是push,也就是有数据就推送到broker,而
consumer到broker的过程是pull,是通过consumer主动去拉数据的,而不是broker把数据主动发送到consumer端的。

简单说下整个系统运行的顺序:
1. 启动zookeeper的server
2. 启动kafka的server
3. Producer如果生产了数据,会先找到broker,热后broker再连接zookeeper,然后将数据存放进broker
4. Consumer如果要消费数据,会先通过zookeeper找对应的broker,然后消费

kafka集群运行的流程图:
这里写图片描述

kafka在zookeeper中的数据:
这里写图片描述

2.2、设计思想

consumer group:各个consumer可以组成一个组,每个消息只能被组中的一个consumer消费,如果一个消息可以被多个consumer消费的话,那么这些consumer必须在不同的组。

消息状态: 在Kafka中,消息的状态被保存在consumer中,broker不会关心哪个消息被消费了被谁消费了,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着如果consumer处理不好的话,broker上的一个消息可能会被消费多次。

消息持久化: Kafka中会把消息持久化到本地文件系统中,并且保持极高的效率。

消息有效期: Kafka会长久保留其中的消息,以便consumer可以多次消费,当然其中很多细节是可配置的。

批量发送: Kafka支持以消息集合为单位进行批量发送,以提高push效率。

push-and-pull: Kafka中的Producer和consumer采用的是push-and-pull模式,即Producer只管向broker push消息,consumer只管从broker pull消息,两者对消息的生产和消费是异步的。

Kafka集群中broker之间的关系: 不是主从关系,各个broker在集群中地位一样,我们可以随意的增加或删除任何一个broker节点。

负载均衡方面: Kafka提供了一个 metadata API来管理broker之间的负载(对Kafka0.8.x而言,对于0.7.x主要靠zookeeper来实现负载均衡)。

同步异步: Producer采用异步push方式,极大提高Kafka系统的吞吐率(可以通过参数控制是采用同步还是异步方式)。

分区机制partition: Kafka的broker端支持消息分区,Producer可以决定把消息发到哪个分区,在一个分区中消息的顺序就是Producer发送消息的顺序,一个主题中可以有多个分区,具体分区的数量是可配置的。分区的意义很重大,后面的内容会逐渐体现。

离线数据装载: Kafka由于对可拓展的数据持久化的支持,它也非常适合向Hadoop或者数据仓库中进行数据装载。


2.3、Kafka的设计特色

Kafka在设计上有以下几个特色:

消息数据通过磁盘线性存取
强调吞吐率
消费状态由消费者自己维护
分布式

消息数据通过磁盘线性存取:

特性应该是Kafka最让惊叹的特性。在我们的认识中,硬盘较内存的数据处理速度(读写)是慢很多的,所以基本所有设计数据处理的程序都是尽量使用内存。然而Kafka的设计者在经过一番调研测试后,大胆地采用了全硬盘存取消息数据的方案,他们的主要依据是:

硬盘在线性读写的情况下性能优异:
有测试表明在一个6 7200rpm 的SATA RAID-5 阵列上,线性写可以达到300MB/Sec,而随机写只有50KB/Sec。线性读写之所以可以有这么优异的表现与文件系统是分不开的,因为对写操作,操作系统一般会进行缓冲,而对于读操作,操作系统会进行预抓取的缓冲操作,这会极大地提高读取效率。又由于这一层缓存操作是在OS级的,也就意味着即便Kafka挂掉了重启,缓存也不会失效。

减少JVM的GC触发:
JVM中的对象会占用除实际数据外的较多空间(如类的信息等等),结构不够紧凑,浪费空间。而当内存中维护的消息数据逐渐增多后,GC便会被频繁触发,这会极大影响应用的响应速度。因此,舍弃内存,使用磁盘可以减少GC触发带来的影响。

Kafka论文中与ActiveMQ等消息队列的性能比较也进一步肯定了Kafka的这一设计理念。

强调吞吐率:

Kafka的设计初衷便是要能处理TB级的数据,其更强调的是吞吐率。吞吐率表现在读和写两个方面,Kafka在这两个方面都做了很多优化。


前面的文章中我们曾经提到过kafka存储消息数据的形式,如下图: topic-partition–0000.kafka –1024.kafka –2048.kafka kafka在启动时会将该文件夹中的所有文件以channel形式打开,并且只有最后一个kafka文件以读写形式打开,其他都以只读方式打开,新到的消息都直接append到最后一个kafka文件中,这样就实现了顺序写。而前面已经提到,顺序写的性能是极高的,这样写的性能就有了保障。


Kafka在读方面使用了sendfile这个高级系统函数,也即zero-copy技术,感兴趣的同学可以去阅读IBM的文章。 这项技术通过减少系统拷贝次数,极大地提高了数据传输的效率。简单说明如下:

程序读文件内容,调用socket发送内容,过程涉及以下几个步骤
调用syscall,如read,陷入内核,读文件内容到内核缓存区pagecache
程序将文件内容由内核缓存区拷贝到用户空间内存调用syscall,如socket write函数,陷入内核,将用户空间的内容拷贝的socket缓冲区内存,准备发送。将socket缓冲区内存拷贝到NIC(Network Interface Controller)缓冲区,发送数据。其中涉及了2次syscall和4次数据拷贝。相信读者一定发现这4次数据拷贝是显然没有必要的,直接把数据从pagecache的内核缓存区读到NIC缓冲区不就可以了吗?sendfile系统函数做的就是这件事情。显然这会极大地提升数据传输的效率。在Java中对应的函数调用是FileChannle.transferTo。
另外,Kafka还通过多条数据压缩传输、存取的办法来进一步提升吞吐率。

消费状态由消费者自己维护:

Kafka消息数据的消费状态由消费者自己维护,原因这里不再详述了,感兴趣的同学可以去阅读文章开头的参考文献。这里简单说一下这么做的好处:
去除了服务端维护消费状态的压力。
提升了消费者存储消费状态的自由度,如存储位置,可以存zookeeper数据库或者HDFS中,根据消费者自身的需求即可。
针对特殊需求,如消息消费失败,消费者可以回滚而重新消费消息。

分布式:

Kafka的broker producer和consumer都是可分布的,其实现是通过zookeeper来维护集群中这三者的信息,从而实现三者的交互,详细实现留待以后再说。

通过上面的介绍相信大家已经对Kafka的设计有了一定的了解。这样的设计完全可以整合offline大数据处理和online实时处理的需求。对于offline处理,kafka支持将数据批量的迁移到hdfs中,而对于online的实时处理,只要合理的设置consumer处理特性就可以很容易地做到。LinkedIn在其文章中曾举到一个kafka的应用案例,这里与大家分享下。使用kafka来更新索引,当用户更新了自己的数据后,producer便产生一条消息发送到broker,consumer会即时获取该数据,进而更新索引,这样也就可以做到搜索时数据”秒级更新“的特性了。

原创粉丝点击