本书以 Kafka 0.10.1.1 版本以基础,对 Kafka 的基本组件的实现细节及其基本应用进行了详细介绍,同时,通过对 Kafka 与当前大数据主流框架整合应用案例的讲解,进一步展现了 Kafka 在实际业务中的作用和地位。按照从抽象到具体、从点到线再到面的学习思维模式,由浅入深,理论与实践相结合,对 Kafka 进行了分析讲解。
本书中的大量实例来源于作者在实际工作中的实践,具有现实指导意义。相信读者阅读完本书之后,能够全面掌握 Kafka 的基本实现原理及其基本操作,能够根据书中的案例举一反三,解决实际工作和学习中的问题。此外,在阅读本书时,读者可以根据本书对 Kafka 理论的分析,再结合 Kafka 源码进行定位学习,了解 Kafka 优秀的设计和思想以及更多的编码技巧。
本书适合应用 Kafka 的专业技术人员阅读,包括但不限于大数据相关应用的开发者、运维者和爱好者,也适合高等院校、培训结构相关专业的师生使用。
牟大恩,武汉大学硕士,曾先后在网易杭州研究院、掌门科技、优酷土豆集团担任高级开发工程师和资深开发工程师职务,目前就职于海通证券总部。有多年的 Java 开发及系统设计经验,专注于互联网金融及大数据应用相关领域。
Kafka 由于高吞吐量、可持久化、分布式、支持流数据处理等特性而被广泛应用。但当前关于 Kafka 原理及应用的相关资料较少,在我打算编写本书时,还没有见到中文版本的 Kafka 相关书籍,对于初学者甚至是一些中高级应用者来说学习成本还是比较高的,因此我打算在对 Kafka 进行深入而系统的研究基础上,结合自己在工作中的实践经验,编写一本介绍 Kafka 原理及其基本应用的书籍,以帮助 Kafka 初、中、高级应用者更快、更好地全面掌握 Kafka 的基础理论及其基本应用,从而解决实际业务中的问题。同时,一直以来我都考虑在技术方面写点什么,将自己所学、所积累的知识沉淀下来。
通过编写本书,我最大收获有如下两点。
第一,凡事不是要尽力而为,而是要全力以赴,持之以恒。写书和阅读源码其实都是很枯燥的事,理工科出身的我,在文字表达能力上还是有所欠缺的,有些知识点可能在脑海里十分清晰,然而当用文字表述出来时,就显得有些“力不从心”了。对于纯技术的东西要用让读者阅读时感觉轻松的文字描述出来更是不易,因此看似简短的几行文字,我在编写时可能斟酌和修改了很久。我真的很钦佩那些大师们,他们写出来的东西总让人很轻松地就能够掌握,“路漫漫其修远兮,吾将上下而求索”,向大师们致敬!虽然有很多客观或主观的因素存在,但我依然没有放弃。还记得2016年10月的一天,当我决定编写本书时,我告诉妻子:“我要写一本书作为送给我们未来宝宝的见面礼!”带着这份动力我利用下班时间、周末时间,在夜深人静时默默地进行着Kafka相关内容的研究、学习、实战,妻子对我的鼓励、陪伴更是激励我要坚持本书的编写。带着这份动力,带着这份爱,我终于完成了本书。
第二,通过对 Kafka 源码的阅读,我除了对很多原来在实践中只知其然而不知其所以然的问题有了更深入的理解以外,还对 Kafka 优秀的设计思想及其编码技巧有所了解。
本书共10章,各章主要内容具体描述如下。
第1章对 Kafka 的基本概念进行简要介绍,方便读者对 Kafka 有一个大致的了解。
第2章详细介绍 Kafka 安装环境的配置及 Kafka 源码的编译,这一章为后续各章的 Kafka 原理讲解及基本操作进行准备。
第3章对 Kafka 基本组件的实现原理、实现细节进行了分析。如果只想了解 Kafka 的相关应用,而不关注 Kafka 的实现原理,在阅读时可以直接跳过这一章。但我觉得,如果想真正掌握 Kafka 及其实现细节,这一章是值得花时间仔细阅读的。
第4章对 Kafka 核心流程进行分析,主要从 Kafka 启动流程到创建一个主题、生产者发送消息、消费者消费消息的过程进行了简要介绍。这一章是 Kafka 运行机制的缩影,如果跳过了第3章关于组件实现原理的讲解,那么建议一定要阅读这一章,因为通过阅读这一章可以更进一步地了解 Kafka 运行时的主要角色及其职责,为后面的 Kafka 实战部分打下坚实基础。
第5章开始就进入了 Kafka 实战部分。这一章通过 Kafka 自带脚本演示,详细介绍了 Kafka 基本应用的操作步骤,基本覆盖了 Kafka 相关操作,因此请读者在阅读时要跟随本书所讲内容进行实战。
第6章对 Kafka 的 API 应用进行了详细介绍。如果读者在实践工作中不会用到调用 Kafka 的相关 API,在阅读时也可以跳过这一章。
第7章对 Kafka Streams 进行了介绍。Kafka Streams 是 Kafka 新增的支持流数据处理的 Java库。如果读者不希望使用此功能,也可以跳过这一章。
第8章介绍 Kafka 在数据采集方面的应用,主要包括与 Log4j、Flume 和 HDFS 的整合应用。
第9章对 Kafka 与 ELK(Elasticsearch、Logstash 和 Kibana)整合实现日志采集平台相关应用进行介绍。
第10章通过两个简单的实例,介绍了 Spark 以及 Kafka 与 Spark 整合在离线计算、实时计算方面的应用。
本书的结构安排上,各章的内容相互独立,因此读者可以首先选择自己最感兴趣的章进行阅读,之后再阅读其他章。例如,读者可以先阅读第5章及其之后的几章,先通过实践操作对 Kafka 有一个感性的认识,然后再阅读第3章和第4章的相关原理及运行机制的内容,逐步加深对 Kafka 实现细节的理解。而第8章至第10章则是 Kafka 与当前大数据处理主流框架的整合应用,属于 Kafka 高级应用部分,可以帮助读者解决实际业务问题。
我建议读者一定要阅读第2章。通过第2章介绍的环境配置,读者能自己在本地搭建 Kafka 运行环境,阅读本书时,可跟随本书所讲解的操作进行实践。
本书的目标读者定位是应用 Kafka 的初、中、高级开发人员及运维工程师。
从事 Kafka 应用开发的技术人员读完本书,可以学习到 Kafka 原理的分析及相关 API 应用以及结合当前主流大数据框架整合的应用,应该能够全面掌握 Kafka 的基本原理和整体结构,并为实际业务实现提供思路,从而能够更加快速地解决一些问题。
从事 Kafka 或数据运维的技术人员,读完本书详细的 Kafka 基本操作以及 Kafka 与其他大数据框架的整合应用案例,应该可以快速搭建、运维和管理 Kafka 及相应的系统平台。
从事 Kafka 相关应用的资深开发或架构人员,读完本书对 Kafka 原理的分析有助于对 Kafka 性能进行调优,可以更好地开发和设计与 Kafka 相关的应用。
对于初学者,通过阅读本书可以全面掌握 Kafka 的知识,同时可以通过 Kafka 与其他框架整合的案例来拓宽视野,为学习分布式相关知识打下基础。
在阅读本书之前,读者需要具备以下基础。
在写作过程当中,我除阅读了 Kafka 源码之外,还从网络上阅读了大量参考资料,从中获得了很多帮助,在此对这些前辈的无私奉献精神表示由衷的钦佩和衷心的感谢。本书参考的资料如下。
书籍
非常高兴能将这本书分享给大家,也十分感谢大家购买和阅读本书。在编写本书时,虽然我精益求精,尽了最大的努力,但由于能力有限,加之时间仓促,书中难免存在不足甚至错误,敬请读者给予指正。如果有任何问题和建议,读者可发送邮件至 [email protected]。
在编写本书时得到了很多人的帮助。
首先我要感谢我的妻子,在我编写本书时你承担了所有家务,让我过着饭来张口、衣来伸手的生活,使我能够全身心投入到写作当中,这本书能够完成有你一半的功劳。也要感谢我的家人,家永远是我心灵的港湾,家人的爱永远是我奋斗的动力。同时也将本书献给我即将出生的宝宝,愿你健康成长,在未来的日子里我会给你更多的惊喜。
然后我特别要感谢人民邮电出版社的杨海玲老师,感谢你一直以来给予我的支持和鼓励,感谢你在本书编写、出版整个过程当中的辛勤付出。也要感谢人民邮电出版社所有参与本书编辑和出版的老师们,正是由于你们的辛勤付出和一丝不苟的工作态度才让本书出版成为可能。
同时要感谢我的工作单位海通证券,公司为我提供了一个非常优越的工作、学习和生活环境。在此要特别感谢部门领导和同事在我编写本书过程中提出很多宝贵的建议,我很荣幸能够与大家成为同事,共同奋斗。
最后我要感谢所有培养过我的老师们,是你们教会了我用知识改变命运,用学习成就未来。
牟大恩
2017年9月于上海
Kafka 是一个高吞吐量、分布式的发布—订阅消息系统。据 Kafka 官方网站介绍,当前的 Kafka 已经定位为一个分布式流式处理平台(a distributed streaming platform),它最初由 LinkedIn 公司开发,后来成为 Apache 项目的一部分。Kafka 核心模块使用 Scala 语言开发,支持多语言(如 Java、C/C++、Python、Go、Erlang、Node.js 等)客户端,它以可水平扩展和具有高吞吐量等特性而被广泛使用。目前越来越多的开源分布式处理系统(如 Flume、Apache Storm、Spark、Flink 等)支持与 Kafka 集成,本书第8章至第10章将通过具体案例详细介绍 Kafka 与当前一些流行的分布式处理系统的集成应用。接下来我们将对 Kafka 相关知识做进一步深入介绍。
随着信息技术的快速发展及互联网用户规模的急剧增长,计算机所存储的信息量正呈爆炸式增长,目前数据量已进入大规模和超大规模的海量数据时代,如何高效地存储、分析、处理和挖掘海量数据已成为技术研究领域的热点和难点问题。当前出现的云存储、分布式存储系统、NoSQL 数据库及列存储等前沿技术在海量数据的驱使下,正日新月异地向前发展,采用这些技术来处理大数据成为一种发展趋势。而如何采集和运营管理、分析这些数据也是大数据处理中一个至关重要的组成环节,这就需要相应的基础设施对其提供支持。针对这个需求,当前业界已有很多开源的消息系统应运而生,本书介绍的 Kafka 就是当前流行的一款非常优秀的消息系统。
Kafka 是一款开源的、轻量级的、分布式、可分区和具有复制备份的(Replicated)、基于 ZooKeeper 协调管理的分布式流平台的功能强大的消息系统。与传统的消息系统相比,Kafka 能够很好地处理活跃的流数据,使得数据在各个子系统中高性能、低延迟地不停流转。
据 Kafka 官方网站介绍,Kafka 定位就是一个分布式流处理平台。在官方看来,作为一个流式处理平台,必须具备以下3个关键特性。
Kafka 能够很好满足以上3个特性,通过 Kafka 能够很好地建立实时流式数据通道,由该通道可靠地获取系统或应用程序的数据,也可以通过 Kafka 方便地构建实时流数据应用来转换或是对流式数据进行响应处理。特别是在0.10版本之后,Kafka 推出了 Kafka Streams,这让 Kafka 对流数据处理变得更加方便。
Kafka 已发布多个版本。截止到编写本书时,Kafka 的最新版本为0.10.1.1,因此本书内容都是基于该版本进行讲解。
通过前面对 Kafka 背景知识的简短介绍,我们对 Kafka 是什么有了初步的了解,本节我们将进一步介绍 Kafka 作为消息系统的基本结构。我们知道,作为一个消息系统,其基本结构中至少要有产生消息的组件(消息生产者,Producer)以及消费消息的组件(消费者,Consumer)。虽然消费者并不是必需的,但离开了消费者构建一个消息系统终究是毫无意义的。Kafka消息系统最基本的体系结构如图1-1所示。
图1-1 Kafka 消息系统最基本的体系结构
生产者负责生产消息,将消息写入 Kafka 集群;消费者从 Kafka 集群中拉取消息。至于生产者如何将生产的消息写入 Kafka,消费者如何从 Kafka 集群消费消息,Kafka 如何存储消息,Kafka 集群如何管理调度,如何进行消息负载均衡,以及各组件间如何进行通信等诸多问题,我们将在后续章节进行详细阐述,在本节我们只需对 Kafka 基本结构轮廓有个清晰认识即可。随着对 Kafka 相关知识的深入学习,我们将逐步对 Kafka 的结构图进行完善。
在对Kafka基本体系结构有了一定了解后,本节我们对Kafka的基本概念进行详细阐述。
Kafka 将一组消息抽象归纳为一个主题(Topic),也就是说,一个主题就是对消息的一个分类。生产者将消息发送到特定主题,消费者订阅主题或主题的某些分区进行消费。
消息是 Kafka 通信的基本单位,由一个固定长度的消息头和一个可变长度的消息体构成。在老版本中,每一条消息称为 Message;在由 Java 重新实现的客户端中,每一条消息称为 Record。
Kafka 将一组消息归纳为一个主题,而每个主题又被分成一个或多个分区(Partition)。每个分区由一系列有序、不可变的消息组成,是一个有序队列。
每个分区在物理上对应为一个文件夹,分区的命名规则为主题名称后接“—”连接符,之后再接分区编号,分区编号从0开始,编号最大值为分区的总数减1。每个分区又有一至多个副本(Replica),分区的副本分布在集群的不同代理上,以提高可用性。从存储角度上分析,分区的每个副本在逻辑上抽象为一个日志(Log)对象,即分区的副本与日志对象是一一对应的。每个主题对应的分区数可以在 Kafka 启动时所加载的配置文件中配置,也可以在创建主题时指定。当然,客户端还可以在主题创建后修改主题的分区数。
分区使得 Kafka 在并发处理上变得更加容易,理论上来说,分区数越多吞吐量越高,但这要根据集群实际环境及业务场景而定。同时,分区也是Kafka保证消息被顺序消费以及对消息进行负载均衡的基础。
Kafka 只能保证一个分区之内消息的有序性,并不能保证跨分区消息的有序性。每条消息被追加到相应的分区中,是顺序写磁盘,因此效率非常高,这是 Kafka 高吞吐率的一个重要保证。同时与传统消息系统不同的是,Kafka 并不会立即删除已被消费的消息,由于磁盘的限制消息也不会一直被存储(事实上这也是没有必要的),因此 Kafka 提供两种删除老数据的策略,一是基于消息已存储的时间长度,二是基于分区的大小。这两种策略都能通过配置文件进行配置,在这里不展开探讨,在3.5.4节将详细介绍。
由于 Kafka 副本的存在,就需要保证一个分区的多个副本之间数据的一致性,Kafka 会选择该分区的一个副本作为 Leader 副本,而该分区其他副本即为 Follower 副本,只有 Leader 副本才负责处理客户端读/写请求,Follower 副本从 Leader 副本同步数据。如果没有 Leader 副本,那就需要所有的副本都同时负责读/写请求处理,同时还得保证这些副本之间数据的一致性,假设有n个副本则需要有n×n条通路来同步数据,这样数据的一致性和有序性就很难保证。
引入 Leader 副本后客户端只需与 Leader 副本进行交互,这样数据一致性及顺序性就有了保证。Follower 副本从 Leader 副本同步消息,对于n个副本只需n−1条通路即可,这样就使得系统更加简单而高效。副本 Follower 与 Leader 的角色并不是固定不变的,如果 Leader 失效,通过相应的选举算法将从其他 Follower 副本中选出新的 Leader 副本。
任何发布到分区的消息会被直接追加到日志文件(分区目录下以“.log”为文件名后缀的数据文件)的尾部,而每条消息在日志文件中的位置都会对应一个按序递增的偏移量。偏移量是一个分区下严格有序的逻辑值,它并不表示消息在磁盘上的物理位置。由于Kafka几乎不允许对消息进行随机读写,因此 Kafka 并没有提供额外索引机制到存储偏移量,也就是说并不会给偏移量再提供索引。消费者可以通过控制消息偏移量来对消息进行消费,如消费者可以指定消费的起始偏移量。为了保证消息被顺序消费,消费者已消费的消息对应的偏移量也需要保存。需要说明的是,消费者对消息偏移量的操作并不会影响消息本身的偏移量。旧版消费者将消费偏移量保存到 ZooKeeper 当中,而新版消费者是将消费偏移量保存到 Kafka 内部一个主题当中。当然,消费者也可以自己在外部系统保存消费偏移量,而无需保存到 Kafka 中。
一个日志又被划分为多个日志段(LogSegment),日志段是 Kafka 日志对象分片的最小单位。与日志对象一样,日志段也是一个逻辑概念,一个日志段对应磁盘上一个具体日志文件和两个索引文件。日志文件是以“.log”为文件名后缀的数据文件,用于保存消息实际数据。两个索引文件分别以“.index”和“.timeindex”作为文件名后缀,分别表示消息偏移量索引文件和消息时间戳索引文件。
在 Kafka 基本体系结构中我们提到了 Kafka 集群。Kafka 集群就是由一个或多个 Kafka 实例构成,我们将每一个 Kafka 实例称为代理(Broker),通常也称代理为 Kafka 服务器(KafkaServer)。在生产环境中 Kafka 集群一般包括一台或多台服务器,我们可以在一台服务器上配置一个或多个代理。每一个代理都有唯一的标识 id,这个 id 是一个非负整数。在一个 Kafka 集群中,每增加一个代理就需要为这个代理配置一个与该集群中其他代理不同的 id,id 值可以选择任意非负整数即可,只要保证它在整个 Kafka 集群中唯一,这个 id 就是代理的名字,也就是在启动代理时配置的 broker.id 对应的值,因此在本书中有时我们也称为 brokerId。由于给每个代理分配了不同的 brokerId,这样对代理进行迁移就变得更方便,从而对消费者来说是透明的,不会影响消费者对消息的消费。代理有很多个参数配置,由于在本节只是对其概念进行阐述,因此不做深入展开,对于代理相关配置将穿插在本书具体组件实现原理、流程分析及相关实战操作章节进行介绍。
生产者(Producer)负责将消息发送给代理,也就是向 Kafka 代理发送消息的客户端。
消费者(Comsumer)以拉取(pull)方式拉取数据,它是消费的客户端。在 Kafka 中每一个消费者都属于一个特定消费组(ConsumerGroup),我们可以为每个消费者指定一个消费组,以 groupId 代表消费组名称,通过 group.id 配置设置。如果不指定消费组,则该消费者属于默认消费组 test-consumer-group。同时,每个消费者也有一个全局唯一的 id,通过配置项 client.id 指定,如果客户端没有指定消费者的 id,Kafka 会自动为该消费者生成一个全局唯一的 id,格式为 ${groupId}-${hostName}-${timestamp}-${UUID前8位字符}
。同一个主题的一条消息只能被同一个消费组下某一个消费者消费,但不同消费组的消费者可同时消费该消息。消费组是 Kafka 用来实现对一个主题消息进行广播和单播的手段,实现消息广播只需指定各消费者均属于不同的消费组,消息单播则只需让各消费者属于同一个消费组。
Kafka 在 ZooKeeper 中动态维护了一个 ISR(In-sync Replica),即保存同步的副本列表,该列表中保存的是与 Leader 副本保持消息同步的所有副本对应的代理节点 id。如果一个 Follower 副本宕机(本书用宕机来特指某个代理失效的情景,包括但不限于代理被关闭,如代理被人为关闭或是发生物理故障、心跳检测过期、网络延迟、进程崩溃等)或是落后太多,则该 Follower 副本节点将从ISR列表中移除。
这里我们并不打算介绍 ZooKeeper 的相关知识,只是简要介绍 ZooKeeper 在 Kafka 中的作用。Kafka 利用 ZooKeeper 保存相应元数据信息,Kafka 元数据信息包括如代理节点信息、Kafka 集群信息、旧版消费者信息及其消费偏移量信息、主题信息、分区状态信息、分区副本分配方案信息、动态配置信息等。Kafka 在启动或运行过程当中会在 ZooKeeper 上创建相应节点来保存元数据信息,Kafka 通过监听机制在这些节点注册相应监听器来监听节点元数据的变化,从而由 ZooKeeper 负责管理维护 Kafka 集群,同时通过 ZooKeeper 我们能够很方便地对 Kafka 集群进行水平扩展及数据迁移。
通过以上 Kafka 基本概念的介绍,我们可以对 Kafka 基本结构图进行完善,如图1-2所示。
图1-2 Kafka 的集群结构
Kafka 的设计初衷是使 Kafka 能够成为统一、实时处理大规模数据的平台。为了达到这个目标,Kafka 必须支持以下几个应用场景。
(1)具有高吞吐量来支持诸如实时的日志集这样的大规模事件流。
(2)能够很好地处理大量积压的数据,以便能够周期性地加载离线数据进行处理。
(3)能够低延迟地处理传统消息应用场景。
(4)能够支持分区、分布式,实时地处理消息,同时具有容错保障机制。
满足以上功能的 Kafka 与传统的消息系统相比更像是一个数据库日志系统。了解了 Kafka 的设计动机之后,在下一节我们将看看 Kafka 发展至今已具有哪些特性。
上一节对 Kafka 的设计动机进行了介绍。随着 Kafka 的不断更新发展,当前版本的 Kafka 又增加了一些新特性,下面就来逐个介绍 Kafka 的这些新特性。
Kafka 高度依赖于文件系统来存储和缓存消息。说到文件系统,大家普遍认为磁盘读写慢,依赖于文件系统进行存储和缓存消息势必在性能上会大打折扣,其实文件系统存储速度快慢一定程度上也取决于我们对磁盘的用法。据 Kafka 官方网站介绍:6块7200r/min SATA RAID-5 阵列的磁盘线性写的速度为600 MB/s,而随机写的速度为100KB/s,线性写的速度约是随机写的6000多倍。由此看来磁盘的快慢取决于我们是如何去应用磁盘。加之现代的操作系统提供了预读(read-ahead)和延迟写(write-behind)技术,使得磁盘的写速度并不是大家想象的那么慢。同时,由于 Kafka 是基于 JVM(Java Virtual Machine)的,而 Java 对象内存消耗非常高,且随着 Java 对象的增加 JVM 的垃圾回收也越来越频繁和繁琐,这些都加大了内存的消耗。鉴于以上因素,使用文件系统和依赖于页缓存(page cache)的存储比维护一个内存的存储或是应用其他结构来存储消息更有优势,因此 Kafka 选择以文件系统来存储数据。
消息系统数据持久化一般采用为每个消费者队列提供一个 B 树或其他通用的随机访问数据结构来维护消息的元数据,B树操作的时间复杂度为O(log n),O(log n)的时间复杂度可以看成是一个常量时间,而且B树可以支持各种各样的事务性和非事务性语义消息的传递。尽管B树具有这些优点,但这并不适合磁盘操作。目前的磁盘寻道时间一般在10ms以内,对一块磁盘来说,在同一时刻只能有一个磁头来读写磁盘,这样在并发IO能力上就有问题。同时,对树结构性能的观察结果表明:其性能会随着数据的增长而线性下降。鉴于消息系统本身的作用考虑,数据的持久化队列可以建立在简单地对文件进行追加的实现方案上。因为是顺序追加,所以Kafka在设计上是采用时间复杂度O(1)的磁盘结构,它提供了常量时间的性能,即使是存储海量的信息(TB 级)也如此,性能和数据的大小关系也不大,同时 Kafka 将数据持久化到磁盘上,这样只要磁盘空间足够大数据就可以一直追加,而不会像一般的消息系统在消息被消费后就删除掉,Kafka 提供了相关配置让用户自己决定消息要保存多久,这样为消费者提供了更灵活的处理方式,因此 Kafka 能够在没有性能损失的情况下提供一般消息系统不具备的特性。
正是由于 Kafka 将消息进行持久化,使得 Kafka 在机器重启后,已存储的消息可继续恢复使用。同时 Kafka 能够很好地支持在线或离线处理、与其他存储及流处理框架的集成。
高吞吐量是 Kafka 设计的主要目标,Kafka 将数据写到磁盘,充分利用磁盘的顺序读写。同时,Kafka 在数据写入及数据同步采用了零拷贝(zero-copy)技术,采用 sendFile() 函数调用,sendFile() 函数是在两个文件描述符之间直接传递数据,完全在内核中操作,从而避免了内核缓冲区与用户缓冲区之间数据的拷贝,操作效率极高。Kafka 还支持数据压缩及批量发送,同时 Kafka 将每个主题划分为多个分区,这一系列的优化及实现方法使得 Kafka 具有很高的吞吐量。经大多数公司对 Kafka 应用的验证,Kafka 支持每秒数百万级别的消息。
Kafka 要支持对大规模数据的处理,就必须能够对集群进行扩展,分布式必须是其特性之一,这样就可以将多台廉价的 PC 服务器搭建成一个大规模的消息系统。Kafka 依赖 ZooKeeper 来对集群进行协调管理,这样使得 Kafka 更加容易进行水平扩展,生产者、消费者和代理都为分布式,可配置多个。同时在机器扩展时无需将整个集群停机,集群能够自动感知,重新进行负责均衡及数据复制。
Kafka 核心模块用 Scala 语言开发,但 Kafka 支持不同语言开发生产者和消费者客户端应用程序。0.8.2 之后的版本增加了 Java 版本的客户端实现,0.10 之后的版本已废弃 Scala 语言实现的 Producer 及 Consumer,默认使用 Java 版本的客户端。Kafka 提供了多种开发语言的接入,如 Java、Scala、C、C++、Python、Go、Erlang、Ruby、Node.js 等,感兴趣的读者可以详见这里。同时,Kafka 支持多种连接器(Connector)的接入,也提供了 Connector API 供开发者调用。Kafka 与当前主流的大数据框架都能很好地集成,如 Flume、Hadoop、HBase、Hive、Spark、Storm 等。
Kafka 在0.10之后版本中引入 Kafak Streams。Kafka Streams 是一个用 Java 语言实现的用于流处理的 jar 文件,关于 Kafka Streams 的相关内容将在第7章中进行讲解。
当前版本的 Kafka 支持以下几种安全措施:
Kafka 可以为每个主题指定副本数,对数据进行持久化备份,这可以一定程度上防止数据丢失,提高可用性。
Kafka 的代理是无状态的,即代理不记录消息是否被消费,消费偏移量的管理交由消费者自己或组协调器来维护。同时集群本身几乎不需要生产者和消费者的状态信息,这就使得 Kafka 非常轻量级,同时生产者和消费者客户端实现也非常轻量级。
Kafka 支持 Gzip、Snappy、LZ4 这3种压缩方式,通常把多条消息放在一起组成 MessageSet,然后再把 MessageSet 放到一条消息里面去,从而提高压缩比率进而提高吞吐量。
消息系统或是说消息队列中间件是当前处理大数据一个非常重要的组件,用来解决应用解耦、异步通信、流量控制等问题,从而构建一个高效、灵活、消息同步和异步传输处理、存储转发、可伸缩和最终一致性的稳定系统。当前比较流行的消息中间件有 Kafka、RocketMQ、RabbitMQ、ZeroMQ、ActiveMQ、MetaMQ、Redis 等,这些消息中间件在性能及功能上各有所长。如何选择一个消息中间件取决于我们的业务场景、系统运行环境、开发及运维人员对消息中件间掌握的情况等。我认为在下面这些场景中,Kafka 是一个不错的选择。
(1)消息系统。Kafka 作为一款优秀的消息系统,具有高吞吐量、内置的分区、备份冗余分布式等特点,为大规模消息处理提供了一种很好的解决方案。
(2)应用监控。利用 Kafka 采集应用程序和服务器健康相关的指标,如 CPU 占用率、IO、内存、连接数、TPS、QPS 等,然后将指标信息进行处理,从而构建一个具有监控仪表盘、曲线图等可视化监控系统。例如,很多公司采用 Kafka 与 ELK(ElasticSearch、Logstash 和 Kibana)整合构建应用服务监控系统。
(3)网站用户行为追踪。为了更好地了解用户行为、操作习惯,改善用户体验,进而对产品升级改进,将用户操作轨迹、内容等信息发送到 Kafka 集群上,通过 Hadoop、Spark 或 Strom 等进行数据分析处理,生成相应的统计报告,为推荐系统推荐对象建模提供数据源,进而为每个用户进行个性化推荐。
(4)流处理。需要将已收集的流数据提供给其他流式计算框架进行处理,用 Kafka 收集流数据是一个不错的选择,而且当前版本的 Kafka 提供了 Kafka Streams 支持对流数据的处理。
(5)持久性日志。Kafka 可以为外部系统提供一种持久性日志的分布式系统。日志可以在多个节点间进行备份,Kafka 为故障节点数据恢复提供了一种重新同步的机制。同时,Kafka 很方便与 HDFS 和 Flume 进行整合,这样就方便将 Kafka 采集的数据持久化到其他外部系统。
本书在结构编排上,先介绍 Kafka 基础知识,接着介绍 Kafka 应用环境搭建,然后对 Kafka 核心组件实现原理进行简要讲解。在核心组件原理讲解之后,又将相应组件应用串起来分析 Kafka 核心流程,之后从 Kafka 基本脚本操作实战开始,结合 Kafka 在实际工作中应用案例详细介绍 Kafka 与当前主流大数据处理框架的应用。同时,将 Kafka Streams 独立成一章进行详细介绍,基本上覆盖了 Kafka Streams 的核心及重要知识的讲解。
为了编写和讲解方便,本书有以下几点约定说明。
(1)本书所讲 Kafka 版本为 0.10.1.1,书中提及的当前版本 Kafka 均指这一版本。
(2)在 Kafka 基本组件实现原理讲解时,为了指明方法所属的对象,本书简单地以“类名.方法名()”的形式说明,这并不表示对类静态方法的调用。同时,鉴于篇幅考虑也省去了方法参数列表,但不代表该方法无参数。
(3)读者在阅读本书时经常会看到“${属性字段}”表达式,本书以此表示该属性字段对应的值。
本章首先对 Kafka 背景及一个简单的 Kafka 消息系统基本结构进行了简单介绍,然后对 Kafka 涉及的基本概念进行了阐述,最后就 Kafka 的设计思想、特性及应用场景进行了归纳。
本章将详细介绍 Kafka 运行环境的搭建,包括在 Linux 系统和 Windows 系统中搭建 Kafka 运行环境。
由于 Kafka 是用 Scala 语言开发的,运行在 JVM 上,因此在安装 Kafka 之前需要先安装 JDK。
最新版本的 Kafka 需要运行在 JDK 1.7 以上,Kafka 官方网站推荐使用 JDK 1.8,因此本书所应用的 JDK 环境采用 JDK 1.8。下面将详细介绍 JDK 1.8 安装步骤。
(1)下载并安装。首先在 Oracle 官方网站 下载 JDK 1.8 安装文件,根据操作系统类型选择相应的 JDK 版本。我使用的是64位操作系统,因此下载 jdk-8u111-windows-x64.exe 安装文件。下载完成后,双击运行安装。在安装时可以选择安装路径,这里在安装时全使用默认路径。
(2)环境变量配置。在系统变量中新增变量名 JAVA_HOME,变量值为 JDK 1.8 安装路径。由于 Java 默认安装在 Program Files 目录下,这个目录名之间有空格,有可能在运行某些应用时因 JDK 安装路径有空格而报错。例如,我在安装 JDK 后,运行 Kafka 时报如下错误:
错误: 找不到或无法加载主类 Files\Java\jdk1.8.0_111\lib\dt.jar;C:\Program
为了避免出现类似的错误,在 Windows 系统上,若 JDK 安装在 Program File 目录下,在设置JAVA_HOME
时,用该目录别名 PROGRA~1,因此将JAVA_HOME
设置为 C:\PROGRA~1\Java\jdk1.8.0_111
。新增变量名 CLASSPATH,变量值为 .;% JAVA_HOME%\lib\dt.jar;% JAVA_HOME%\ lib\tools.jar
。本步操作 JDK 环境变量配置如表2-1所示。
表2-1 JDK 环境变量配置
变 量 类 型 | 变 量 名 | 变 量 值 |
---|---|---|
系统变量 | JAVA_HOME | C:\PROGRA~1\Java\jdk1.8.0_111 |
用户变量 | CLASSPATH | .;% JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar |
(3)验证。环境变量配置完成后,在 Windows 的 cmd 终端输入查看 Java 版本的命令,以此来验证 JDK 安装配置是否成功。命令如下:
Java –version
若输出为以下 JDK 版本信息,则表示 JDK 1.8 已安装成功,且为系统默认 JDK。
Java version "1.8.0_111"Java(TM) SE Runtime Environment (build 1.8.0_111-b14)Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
一些 Linux 的发行版默认已安装了 JDK,如 OpenJDK,这里所用的 Linux 操作系统默认已经安装了 OpenJDK。输入下面的命令查看JDK版本信息:
Java –version
输出 JDK 版本信息如下:
Java version "1.7.0_45"OpenJDK Runtime Environment (rhel-2.4.3.3.el6-x86_64 u45-b15)OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)
然而有些 Linux 系统并没有安装 JDK,因此本小节将详细讲解如何在 Linux 中安装 JDK。这里我们讲解 JDK 1.8 的安装。
(1)下载并安装。进入 Oracle 官方网站 下载 Linux 版本的 JDK 1.8 安装包。这里我们下载的安装包版本为 jdk-8u111-linux-x64.gz,并将安装包解压到 /usr/local/software/Java 路径下。
tar -xzvf jdk-8u111-linux-x64.gz # 解压jdk安装包
将安装包解压后,即完成 JDK 的安装。
(2)配置环境变量。在 /etc/profile 文件中添加 JDK 和 JRE 的路径,并添加到 Path 中,操作命令如下:
vi /etc/profile # 编辑profile文件
在文件中添加以下内容:
export JAVA_HOME=/usr/local/software/Java/jdk1.8.0_111export JRE_HOME=/usr/local/software/Java/jdk1.8.0_111/jreexport PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
保存退出。若系统默认安装了 OpenJDK,则用户可以选择将其删除,也可以修改配置用最新安装的版本替换 OpenJDK。这里选择用新安装的 JDK 替换系统自带的 OpenJDK,则按序执行以下命令。
update-alternatives --install /usr/bin/Java Java /usr/local/software/Java/jdk1.8.0_111 300
update-alternatives --install /usr/bin/Javac Javac /usr/local/software/ Java/jdk1.8.0_111/bin/Javac 300
update-alternatives --config Java
执行以上命令会出现 JDK 版本选择界面,如图2-1所示。这里我们选择新安装的 JDK 1.8,即输入序号3,按回车键。
图2-1 Linux 控制台展示的 JDK 版本选择命令行界面
环境变量配置好后执行以下命令,让刚才的修改操作立即生效:
source /etc/profile # 让对/etc/profile的修改立即生效
(3)验证。输入查看 JDK 版本命令,查看环境变量配置是否成功,执行以下命令:
Java –version # 查看jdk版本
输出以下 JDK 版本信息:
Java version "1.8.0_111"Java(TM) SE Runtime Environment (build 1.8.0_111-b14)Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
由以上 JDK 版本信息可知,JDK 版本已替换为新安装的 JDK 1.8版本。至此,JDK 安装完成。
对 Kafka 集群本身来讲,配置 SSH 免密钥登录并不是必需的步骤,但作为分布式系统,一般会由多台机器构成。为了便于操作管理,如通过 SSH 方式启动集群代理等,这里对 SSH 安装配置进行介绍。
(1)在根目录下查看是否存在一个隐藏文件夹.ssh。若没有该文件夹,则在确保机器联网条件下执行以下命令安装 ssh:
sudo apt-get install ssh # 安装ssh
(2)进入.ssh 目录,生成密钥对,执行命令如下:
ssh-keygen -t rsa # 产生密钥
在执行以上命令时一路回车即可。ssh-keygen 用于生成认证密钥,-t 用来指定密钥类型,这里选择 rsa 密钥。执行完毕后会在 ~/.ssh 目录下生成 id_rsa
和id_rsa.pub
两个文件,其中id_rsa
为私钥文件,id_rsa.pub 为公钥文件。依次在集群其他机器上完成步骤1和步骤2。
(3)将 id_rsa.pub 文件内容追加到授权的 key 文件中,命令如下:
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys # 追加公钥到授权文件中
若是单机环境,则至此已完成 ssh 配置。
(4)将第一台机器的 authorized_keys
文件复制到第二台机器上,并将第二台机器的公钥也追加到 authorized_keys 文件中,依次执行以下命令:
scp authorized_keys [email protected]:~/.ssh/ # 复制第一台机器的授权文件到第二台机器cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys # 在第二台机器上执行此命令,将第二台机器的公钥追加到授权文件中
(5)将第二台机器的authorized_keys
文件复制到第三台机器上,并将第三台机器的公钥追加到 authorized_keys 文件中,执行命令如下:
scp authorized_keys [email protected]:~/.ssh/ # 复制第二台机器的授权文件到第三台机器cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys # 在第三台机器上执行此命令,将第三台机器的公钥追加到授权文件中
若集群还有更多机器,则依此类推完成授权文件合并。至此 ssh 配置完成,在已配置 ssh 的任何一台机器上均可免密登录到其他机器。例如,在第一台机器上执行以下 ssh 命令,输出如下(首次登录会让输入密码):
[root@rhel65 .ssh]# ssh 172.117.12.61The authenticity of host '172.117.12.61 (172.117.12.61)' can't be established.RSA key fingerprint is a3:5b:a9:29:ed:00:74:f4:ce:51:e5:7c:42:5b:8d:44.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '172.117.12.61' (RSA) to the list of known [email protected]'s password: Last login: Wed Feb 8 17:30:11 2017 from server-1[root@rhel65 ~]# ssh 172.117.12.61Last login: Wed Feb 8 17:32:04 2017 from server-3[root@rhel65 ~]# ssh 172.117.12.62Last login: Wed Feb 8 17:26:09 2017 from server-1
ZooKeeper 是一个分布式应用程序协调服务框架,分布式应用程序可以基于 ZooKeeper 来实现同步服务、配置维护、命名服务等,ZooKeeper 能提供基于类似于文件系统的目录节点树方式的数据存储,通过监控各节点数据状态的变化,达到基于数据的集群管理。ZooKeeper 主要由表2-2所示的几个角色构成。
表2-2 ZooKeeper 集群主要角色说明
角 色 | 描 述 | |
---|---|---|
Leader | 集群的领导者,负责投票的发起和决议及更新系统状态 | |
Learner | Follower | 跟随者,接受客户端的请求并返回结果给客户端,参与投票 |
Observer | 接受客户端的请求,将写的请求转发给 Leader,不参与投票。Observer 目的是扩展系统,提高读的速度 |
关于 ZooKeeper 的原理及其他相关知识,读者可查阅 ZooKeeper 官方网站及相关书籍进行深入了解。
Kafka 依赖 ZooKeeper,通过 ZooKeeper 来对代理、消费者上下线管理、集群、分区元数据管理等,因此 ZooKeeper 也是 Kafka 得以运行的基础环境之一。
进入 ZooKeeper 官方网站下载 ZooKeeper(本书所用 ZooKeeper 版本为 zookeeper-3.4.8),然后将下载文件解压到指定目录。对 ZooKeeper 的安装,下面按 Windows 和 Linux 分别进行讲解。
一般会选择在 Linux 操作系统上安装和部署分布式服务,因此这里并不打算讲解 Windows 环境下 ZooKeeper 集群环境搭建,只是简单介绍 Windows 环境下 ZooKeeper 单机模式的安装。
(1)解压安装。首先将 ZooKeeper 安装包 zookeeper-3.4.8.tar.gz 解压到相应目录,这里将 ZooKeeper 解压到 D:\software\zookeeper-3.4.8 目录下。然后进入 ZooKeeper 安装路径 conf 目录下,会看到 ZooKeeper 提供了一个 zoo_sample.cfg 的配置模板,将该文件重命名为 zoo.cfg。zoo.cfg 文件中只需修改 dataDir 和 dataLogDir 配置,其他配置使用默认值(其他配置及其含义将在下面的“Linux 搭建 ZooKeeper 环境”小节详细介绍)。这里对 dataDir 和 dataLogDir 配置如下:
dataDir=F:\\zookeeper\\datadataLogDir=F:\\zookeeper\\logs
至此,Windows 环境下 ZooKeeper 安装配置完成。下面进入 ZooKeeper 安装路径 bin 目录下,启动及验证 ZooKeeper 安装是否成功。
(2)验证。执行启动 ZooKeeper 命令:
zkServer.cmd # windows下启动ZooKeeper
若输出没有任何错误,通过 jps 命令可以看到 ZooKeeper 相关进程。输入命令:
jps # 查看Java进程命令
输出结果中至少包括以下进程名:
12008 QuorumPeerMain11596 Jps
还可以进入 ZooKeeper 的安装路径 bin 目录下,通过 ZooKeeper 客户端连接到 ZooKeeper 服务,执行以下命令进一步验证 ZooKeeper 是否安装成功:
zkCli.cmd -server 127.0.0.1:2181 # 登录到ZooKeeper服务器
在输出信息中会看到“Welcome to ZooKeeper!”,同时显示接受命令输入界面。
在客户端输入:
ls / # 查看 ZooKeeper 服务器目录结构
此时 ZooKeeper 服务器中仅有一个 zookeeper 节点,信息显示如下:
[zk: 127.0.0.1:2181(CONNECTED) 0] ls /[zookeeper]
至此,Windows 环境下安装 ZooKeeper 讲解完毕。
在 Linux 环境下 ZooKeeper 单机模式配置与上一小节介绍的 Windows 环境下 ZooKeeper 安装配置的操作步骤基本相同,因此本小节直接介绍 ZooKeeper 分布式环境搭建。下面将讲解在 Linux 环境下如何配置由3台机器构成的 ZooKeeper 集群环境,这3台机器的 IP 地址分别为 172.117.12.61、172.117.12.62 和 172.117.12.63。
(1)解压安装。首先在3台机器上分别将 zookeeper-3.4.8.tar.gz 解压到 /usr/local/software/ zookeeper 目录。进入解压后的 zookeeper-3.4.8 /conf 目录,将 zoo.sample.cfg 重命名为 zoo.cfg。关于 ZooKeeper 配置文件中几个基础配置项的说明如表2-3所示。
表2-3 ZooKeeper 基础配置说明
配 置 项 | 默 认 值 | 说 明 |
---|---|---|
tickTime | 2000ms | ZooKeeper 中的一个时间单元。ZooKeeper 中所有时间都以这个时间单元为基准,进行整数倍配置,默认是2 s |
initLimit | 10 | Follower 在启动过程中,会从 Leader 同步所有最新数据,确定自己能够对外服务的起始状态。当 Follower 在 initLimt个tickTime 还没有完成数据同步时,则 Leader 仍为 Follower 连接失败 |
syncLimit | 5 | Leader 与 Follower 之间通信请求和应答的时间长度。若 Leader 在 syncLimit 个 tickTime 还没有收到 Follower 应答,则认为该 Leader 已下线 |
dataDir | /tmp/zookeeper | 存储快照文件的目录,默认情况下,事务日志也会存储在该目录上。由于事务日志的写性能直接影响 ZooKeeper 性能,因此建议同时配置参数 dataLogDir |
dataLogDir | /tmp/zookeeper | 事务日志输出目录 |
clientPort | 2181 | ZooKeeper 对外端口 |
请读者根据自已服务器环境,修改 zoo.cfg 文件中表2-3提及参数的配置。这里只修改了以下两个配置项,其他几个基础配置沿用默认值。
dataDir=/opt/data/zookeeper/datadataLogDir=/opt/data/zookeeper/logs
若是单机模式,操作至此完成。接下来配置将3台机器构成一个分布式集群。
(2)集群配置。首先在3台机器的/etc/hosts文件中加入3台机器的 IP 与机器域名映射,域名自定义,这里分别命名为 server-1、server-2、server-3,3台机器 IP 与机器域名映射关系如下:
172.117.12.61 server-1172.117.12.62 server-2172.117.12.63 server-3
然后进入其中一台机器的 ZooKeeper 安装路径 conf 目录。这里我们选择在 IP 为 172.117.12.61 的机器上进行配置,编辑 conf/zoo.cfg 文件,在该文件中添加以下配置:
server.1=server-1:2888:3888server.2=server-2:2888:3888server.3=server-3:2888:3888
为了便于讲解以上配置,在这里抽象一个公式,即 server.n=n-server-domain:port1:port2。这个公式中的 n 是一个数字类型常量,这里配置的1、2和3用于表示第几台 ZooKeeper 服务器;n-server-domain 表示第 n 台 ZooKeeper 服务器的 IP 所映射的域名,当然这里也可以是第 n 台机器的 IP;port1 表示该服务器与集群中的 Leader 交换信息的端口,默认是 2888;port2 表示选举时服务器相互通信的端口。
接着在${dataDir}
路径下创建一个 myid 文件。myid 里存放的值就是服务器的编号,即对应上述公式中的 n,在这里第一台机器 myid 存放的值为1。ZooKeeper 在启动时会读取 myid 文件中的值与 zoo.cfg 文件中的配置信息进行比较,以确定是哪台服务器。
在 zoo.cfg 文件中我们同时配置了3台机器,因此接下来通过 scp 命令将本台机器的 zoo.cfg 文件复制到另外两台机器相应目录进行替换。
scp zoo.cfg [email protected]:/usr/local/software/zookeeper/zookeeper-3.4.8/conf/scp zoo.cfg [email protected]:/usr/local/software/zookeeper/zookeeper-3.4.8/conf/
然后分别修改另外两台机器的 myid。同时,为了操作方便,我们将 ZooKeeper 相关环境变量添加到 /etc/profile 文件当中。
设置 ZooKeeper 安装路径,在 /etc/profile 相关环境变量配置中添加以下信息:
export ZOOKEEPER_HOME=/usr/local/software/zookeeper/zookeeper-3.4.8
在该文件的 Path 配置项最后加上:$ZOOKEEPER_HOME/bin
。注意,在$ZOOKEEPER_HOME
前有一个冒号。然后执行 source/etc/profile 命令使所做的修改操作立即生效。其他两台机器也进行同样的环境设置。至此,由3台机器构成的分布式 ZooKeeper 环境搭建步骤介绍完毕。下面启动 ZooKeeper 进行验证。
(3)验证。由于配置了 ZooKeeper 环境变量,因此无需进入 ZooKeeper 安装路径 bin 目录下。在这3台机器上分别启动 ZooKeeper:
zkServer.sh start # 启动ZooKeeper服务
输出如下信息:
ZooKeeper JMX enabled by defaultUsing config: /usr/local/software/zookeeper/zookeeper-3.4.8/bin/../conf/zoo.cfgStarting zookeeper ... STARTED
查看这3台 ZooKeeper 服务器状态,依次在这3台机器上执行以下命令:
zkServer.sh status # 查询zookeeper状态
执行上述启动命令,其中有两台机器输出以下信息:
ZooKeeper JMX enabled by defaultUsing config: /usr/local/software/zookeeper/zookeeper-3.4.8/bin/../conf/zoo.cfgMode: follower
另外一台机器输出信息如下:
ZooKeeper JMX enabled by defaultUsing config: /usr/local/software/zookeeper/zookeeper-3.4.8/bin/../conf/zoo.cfgMode: leader
可以看到,这3台机器中,一台机器作为 Leader,其他两台服务器作为 Follower。同时,可以查看 zookeeper.out 文件内容,通过启动日志进一步了解 ZooKeeper 运行过程。至此,ZooKeeper 集群环境搭建讲解完毕。
Kafka 安装较简单,不同操作系统下安装步骤基本相同,针对大多数用户来讲,在生产环境使用 Kafka 一般选择 Linux 服务器,本书 Kafka 实战操作也是基于 Linux 环境进行讲解的。下面分别介绍 Kafka 在 Windows 操作系统以及 Linux 操作系统的安装步骤,Mac 操作系统的安装步骤与 Linux 操作系统的安装步骤基本类似,不再介绍。同时,后面几节中的 Kafka 集群环境搭建也只介绍在 Linux 环境下 Kafka 集群环境的部署。
Windows 下安装 Kafka 只需将下载的 Kafka 安装包解压到相应目录即可。
(1)下载及安装。进入 Kafka 官方网站下载当前最新版本的 Kafka,Kafka 安装包并没有区分 Windows 安装包还是 Linux 安装包,仅在 bin 目录下将 Windows 环境执行 Kafka 的相关脚本放在 /bin/windows 目录下。当前 Kafka 最新版本为 kafka_2.11-0.10.1.1.tgz
,其中 2.11 代表 Scala 版本,0.10.1.1 表示 Kafka 的版本。这里将下载的安装包解压到 D:\software\kafka_2.11-0.10.1.1
目录下,为了便于讲解,这里记 Kafka 安装路径为$KAFKA_HOME
。至此,Windows 下 Kafka 完成安装。当然我们也可以像安装 JDK 一样配置 Kafka 环境变量,感兴趣的读者可以自行配置,这步操作不是必需的步骤,因此不再阐述。
(2)启动 KafkaServer 验证。安装好 Kafka 后,启动 KafkaServer。在启动 Kafka 之前,需要启动 Zoookeeper。若 ZooKeeper 服务不是本地服务,应修改 Kafka 安装目录下 /config/server.properties 文件 zookeeper.connect 配置项,然后在 Windows 的 cmd 下进入$KAFKA_HOME/bin/windows
目录,执行以下命令,启动 KafkaServer。
kafka-server-start.bat ../../config/server.properties # windows下启动kafak server
若在启动过程中没有报任何异常信息,同时在控制台最后输出打印内容如图2-2所示,则表示 Kafka 在 Windows 环境下安装成功。
图2-2 KafkaServer 启动日志
在 Linux 系统上安装 Kafka 与在 Windows 系统上安装操作基本相同,将安装包解压到相应目录,这里依然将 Kafka 安装目录记为$KAFKA_HOME
,修改$KAFKA_HOME/config/server.properties
文件相关配置即可。这里安装 Kafka 所用机器与安装 ZooKeeper 的机器相同,但在生产环境,一般将 ZooKeeper 集群与 Kafka 机器分机架部署。在讲解 Kafka 单机版本安装时,我们选择3台机器中的一台,IP 为 172.117.12.61。
(1)解压安装。先将 Kafka 安装包 kafka_2.11-0.10.1.1.tgz 解压到指定目录下,这里将 Kafka 解压到 /usr/local/software/kafka 目录下。进入 /usr/local/software/kafka 目录执行以下命令解压 Kafka 安装包。
tar -xzvf kafka_2.11-0.10.1.1.tgz # 解压安装Kafka
由于后续对 Kafka 的讲解都是在 Linux 环境下,因此为了操作方便我们对 Kafka 的环境变量进行设置。在 /etc/profile 文件中加入 Kafka 安装路径,并将 Kafka 的 bin 目录添加进 Path 中。这一步操作并非 Kafka 安装必需的设置,读者可根据情况选择是否需要对 Kafka 环境变量进行配置。打开 /etc/profile 文件添加以下配置。
export KAFKA_HOME=/usr/local/software/kafka/kafka_2.11-0.10.1.1
:$KAFKA_HOME/bin
,添加 Kafka bin 后完整的 Path 如下: export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin:$KAFKA_HOME/bin
保存文件退出,执行 source /etc/profile 命令让刚才新增的 Kafka 环境变量设置生效。再在任一路径下输入 kafka 然后按 Tab 键,会提示补全 Kafka 运行相关脚本.sh 文件,表示 Kafka 环境变量配置成功,但一般 Kafka 脚本运行时会加载 /config 路径下的相关配置文件,因此当不在 Kafka 安装目录 bin 下执行相关脚本时,需要指定配置文件绝对路径。
(2)修改配置。修改$KAFKA_HOME/config
目录下的 server.properties 文件,为了便于后续集群环境搭建的配置,需要保证同一个集群下 broker.id 要唯一,因此这里手动配置 broker.id,直接保持与 ZooKeeper 的 myid 值一致,同时配置日志存储路径。server.properties 修改的配置如下:
broker.id=1 # 指定代理的 idlog.dirs=/opt/data/kafka-logs # 指定 Log 存储路径
其他配置保持不变,由于 172.117.12.61 这台机器本地已安装了 ZooKeeper,因此在 Kafka 单机版本安装讲解时,我们暂不对 zookeeper.connect 配置进行修改,其他配置文件也暂不进行修改。
(3)验证。
$KAFKA_HOME/ bin
目录下,执行启动 KafkaServer 命令。 kafka-server-start.sh -daemon ../config/server.properties # 启动Kafka
执行 jps 命令查看 Java 进程,此时进程信息至少包括以下几项:
15976 Jps 14999 QuorumPeerMain 15906 Kafka
可以看到 ZooKeeper 进程和 Kafka 进程名,同时进入$KAFKA_HOME/logs
目录下,查看 server.log 会看到 KafkaServer 启动日志,在启动日志中会记录 KafkaServer 启动时加载的配置信息。
zkCli.sh -server server-1:2181 # 登录ZooKeeper ls / # 查看ZooKeeper目录结构
在 Kafka 启动之前 ZooKeeper 中只有一个 zookeeper 目录节点,Kafka 启动后目录节点如下:
[cluster, controller, controller_epoch, brokers, zookeeper, admin, isr_change_ notification, consumers, config]
执行以下命令,查看当前已启动的 Kafka 代理节点:
ls /brokers/ids # 查看已启动的代理节点 [1] # 已启动的代理节点对应的 brokerId
输出信息显示当前只有一个 Kafka 代理节点,当前代理的 brokerId 为1。至此,Kafka 单机版安装配置介绍完毕,相关操作我们将在第5章进行详细介绍。
KafkaServer 启动时需要加载一个用于 KafkaServer 初始化相关配置的 server.properties 文件,当然文件名可以任意,一个 server.properties 对应一个 KafkaServer 实例。Kafka 伪分布式就是在一台机器上启动多个 KafkaServer 来达到多代理的效果,因此要保证 broker.id 及 port 在同一台机器的多个 server.properities 中唯一。
本节在上一节的 Linux 安装 Kafka 基础配置之上,将 server.properties 文件复制一份并命名为 server-0.properties,在 server-0.properties 文件中修改配置如下:
broker.id=0log.dirs=/opt/data/kafka-logs/broker-0port=9093
由于代理默认端口是 9092,server.properties 没有设置端口则采用默认设置,因此在 server-0.properties 将 port 设置为 9093。这个端口可以自定义,只要新端口没有被占用即可。执行以下命令,分别启动 brokerId 为 0 和 1 的两个 KafkaServer:
kafka-server-start.sh -daemon ../config/server-0.properties # 启动代理0kafka-server-start.sh -daemon ../config/server.properties # 启动代理1
再次执行 jps 命令查看 Java 进程信息,打印输出如下信息:
19453 Kafka18036 ZooKeeperMain18228 QuorumPeerMain19169 Kafka19504 Jps
从输出的进程信息可以看到有两个 Kafka 进程存在,即代表刚才启动的 broker.id 为 0 和 1 的两个代理。此时登录 ZooKeeper 客户端,再查看 ZooKeeper 的 /brokers/ids 目录,会看到该目录下有两个节点:
[zk: 172.117.12.61(CONNECTED) 0] ls /brokers/ids[0, 1]
这样,一台机器上启动多个代理的伪分布式环境安装配置介绍完毕。
2.2.2节已经讲解了 Kafka 单机版安装配置,因此对 Kafka 集群环境配置时只需将单机版安装的 Kafka 配置进行相应修改,然后复制到另外两台机器即可。这里只需修改 server.properties 文件中 Kafka 连接 ZooKeeper 的配置,将 Kafka 连接到 ZooKeeper 集群,配置格式为“ZooKeeper 服务器 IP:ZooKeeper 的客户端端口”,多个 ZooKeeper 机器之间以逗号分隔开。
zookeeper.connect=server-1:2181,server-2:2181,server-3:2181
进入 172.117.12.61 服务器 /usr/local/software 目录下,执行以下两条命令将本机安装的 Kafka 分别复制到另外两台服务器上:
scp -r kafka_2.11-0.10.1.1 [email protected]:/usr/local/software/kafkascp -r kafka_2.11-0.10.1.1 [email protected]:/usr/local/software/kafka
复制完成后,分别登录另外两台机器,修改 server.properties 文件中的 broker.id 依次为2和3。当然可以设置任一整数,只要保证一个集群中 broker.id 唯一即可。同时在3台机器的 server.properties 文件中设置 host.name 为本机的 IP。例如,对主机名为 server-1 的机器上的 Kafka 节点,在 server.properties 文件中增加 host.name=172.117.12.61。本书所用版本的 Kafka 若不设置 host.name,则会在创建主题及向主题发送消息时发生NOT_LEADER_FOR_PARTITION
这样的异常。
配置完毕后,分别启动3台机器的 KafkaServer,通过 ZooKeeper 客户端查看 Kafka 在 ZooKeeper 中的相应元数据信息,其中查看 /brokers/ids 节点信息如下:
[zk: 172.117.12.61(CONNECTED) 1] ls /brokers/ids[1, 2, 3]
由 /brokers/ids 节点存储的元数据可知,3台机器的 Kafka 均已正常已启动。至此,Kafka 分布式环境搭建过程介绍完毕。
在实际应用中,我们经常需要了解集群的运行情况,如查看集群中代理列表、主题列表、消费组列表、每个主题对应的分区列表等,抑或是希望通过简单的Web界面操作来创建一个主题或是在代理负载不均衡时,手动执行分区平衡操作等。为了方便对 Kafka 集群的监控及管理,目前已有开源的 Kafka 监控及管理工具,如 Kafka Manager、Kafka Web Console、KafkaOffsetMonitor 等,读者也可以根据自己业务需要进行定制开发。本节只简单讲解 Kafka Manager 的安装应用。
Kafka Manager 由 yahoo 公司开发,该工具可以方便查看集群主题分布情况,同时支持对多个集群的管理、分区平衡以及创建主题等操作。读者可访问这里进行深入了解。
(1)下载编译 Kafka Manager。进入 GitHut 官网搜索关键词“kafka-manager”即可查询到 Kafka Manager 的下载地址,直接点击“Clone or download”按钮进行下载。将下载的 kafka-manager-master.zip 文件上传到 Linux 服务器。用户也可以在 Linux 机器上执行以下命令在线下载 Kafka Manager 源码:
git clone https://github.com/yahoo/kafka-manager # 从GitHub上下载Kafka Manager源码
Kafka Manager 是用 Scala 语言开发的,通过 sbt(Simple Build Tool) 构建,sbt 是对 Scala 或 Java 语言进行编译的一个工具,它类似于 Maven,Gradle。截止到编写本书时,Kafka Manager 是基于 0.9.0.1 版本的 Kafka 开发的,鉴于 Kafka 0.9 与 Kafka-0.10 版本的实现,该版本的 Kafka Manager 也能作为 0.10.+ 版本的 Kafka 管理及监控工具,在 Kafka Manager 管理界面添加集群管理配置时,Kafka Version 选0.9.0.1即可。待源码下载之后,进入 Kafka Manager 源码目录,会有一个 sbt 文件,执行以下命令进行 Kafka Manager 源码编译。
./sbt clean dist # 编译Kafka Manager源码
编译过程会下载相关的 jar 文件,因此有些耗时。等源码编译完成后,在控制台输出的编译日志的最后几行信息如下:
[info] Your package is ready in /home/morton/.sbt/0.13/staging/17dfe5a6b216985c290a/ kafka-manager-master/target/universal/kafka-manager-1.3.2.1.zip[info] [success] Total time: 170 s, completed 2017-1-15 14:23:45
从控制台输出的编译日志信息可以看到,在编译时会在 /home/用户名/路径下创建一个.sbt 目录,编译后的文件存放在该目录相应子目录里,编译日志信息中的 morton 为编译 Kafka Manager 源码的机器名。在编译过程中出现:
Download failed. Obtain the jar manually and place it at /home/morton/.sbt/launchers/ 0.13.9/sbt-launch.jar
表示在编译过程下载 sbt-launch.jar 文件遇到问题,请读者单独下载 sbt-launch.jar 相应版本并上传到/home/用户名/.sbt/launchers/0.13.9/目录下,再次执行编译命令。最终会在 /home/用户名/.sbt/0.13/staging 相应子目录下生成 kafka-manager-1.3.2.1.zip 文件,该文件就是用来对 Kafka 进行监控和管理的工具。若读者在编译时由于个人网络环境原因无法编译,可以直接在网络上下载该文件然后复制到服务器。将编译好的 kafka-manager-1.3.2.1.zip 文件解压到指定位置(这里解压到 /usr/local/software/kafka-manager 目录下)即完成安装。
(2)修改配置。进入 Kafka Manager 安装路径下的 conf 目录,打开 application.conf 文件,修改以下配置。将 kafka-manager.zkhosts="kafka-manager-zookeeper:2181" 配置项,修改为实际的 ZooKeeper 连接地址,例如这里修改为:
kafka-manager.zkhosts="172.117.12.61:2181,172.117.12.62:2181,172.117.12.63:2181"
(3)启动Kafka Manager。进入 bin 目录下执行以下启动命令:
nohup ./kafka-manager -Dconfig.file=../conf/application.conf & # 启动Kafka Manager
Kafka Manager 默认请求端口是 9000,在浏览器中输入安装 Kafka Manager 服务地址及9000端口访问 Kafka Manager,如访问 http://172.117.12.62:9000。Kafka Manager 启动初始化界面如图2-3所示。
图2-3 Kafka Manager 启动初始化界面
通过修改配置文件 application.conf 里 http.port 的值,或是通过命令行参数传递可以修改 Kafka Manager 访问端口。例如,在启动时指定端口为9001,启动命令如下:
nohup ./kafka-manager -Dhttp.port=9001 -Dconfig.file=../conf/application.conf 修改Kafka Manager外部访问端口号为9001
(4)关闭 Kafka Manager。Kafka Manager 没有提供关闭操作的执行脚本及命令,当希望关闭 Kafka Manager 时,可直接通过 kill 命令强制杀掉 Kafka Manager 进程。
查看 Kafka Manager 进程,输入 jps 命令,输出以下进程信息:
767 ProdServerStart12422 QuorumPeerMain13348 Kafka895 Jps
其中 ProdServerStart 即为 Kafka Manager 进程。通过 kill 命令关闭 Kafka Manager:
kill -9 767 # 关闭Kafka Manager进程
同时,由于 Kafka Manager 运行时有一个类似锁的文件RUNNING_PID
,位于 Kafka Manager 安装路径 bin 同目录下,为了不影响下次启动,在执行 kill 命令后同时删除 RUNNING_PID 文件,命令如下:
rm –f RUNNING_PID # 删除Kafka Manager运行时的PID文件
否则,在下次启动时会由于以下错误而导致 Kafka Manager 无法启动。错误信息如下:
This application is already running (Or delete /usr/local/software/kafka-manager/ RUNNING_PID file).
若想在 Kafka Manager 监控中能展示更多的信息,则在 Kafka 启动时启动 JMX。至此,Kafka Manager 安装讲解完毕,对于 Kafka Manager 的相关操作将在5.8节进行介绍。
要研究 Kafka,阅读 Kafka 源码是必不可少的环节。因此,本节将介绍 Kafka 源码编译及将编译后的源码导入 Eclipse 的具体步骤。当然也可以将 Kafka 源码导入其他 IDE(如 Intellij Idea、STS 等)中,大家选用自己惯用的 IDE 即可。源码导入步骤与导入 Eclipse 操作基本类似,本书不再做详细介绍。这里只讲解在 Windows 操作系统下 Kafka 源码的编译,在其他操作系统上对 Kafka 源码的编译操作基本类似,只不过添加环境变量操作有所不同,这里不做讲解,读者可以查阅相关资料进行了解。由于 Kafka 核心模块是用 Scala 语言开发,用 Gradle 编译和构建的,因此下面先介绍相关环境的安装配置。
由于 0.10.1.1 版本的 Kafka 需要 Scala 版本在 2.10 以上,因此这里选择 scala-2.11.8 版本进行安装。
(1)下载并安装。先进入 Scala 官方网站下载相应的安装包,下载图2-4所示版本的 Scala。
图2-4 Scala 安装包下载列表
下载完成后,直接将安装包解压到指定目录即完成安装,安装时解压到 D:\software\scala- 2.11.8 目录下。
(2)环境变量配置。安装完成后,配置 Scala 运行环境变量,在系统变量中新增 Scala 安装路径配置,编辑系统变量配置如图2-5所示。
然后将;%SCALA_HOME%\bin
添加到用户变量 path 中。与 JDK 环境安装配置一样,直接添加至自定义的用户环境变量 CLASSPATH 中,如图2-6所示。
图2-5 新建SCALA_HOME变量指定Scala安装路径
图2-6 修改 CLASSPATH 添加 Scala 环境变量
(3)验证。Scala 安装及环境变量配置完成后,在 Windows 下打开一个 cmd 命令行终端。输入查看 Scala 版本信息的命令:
scala -version # 查询Scala版本
若输出以下信息则表示 Scala 安装配置成功:
Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL
进入 Gradle 官方网站下载 Gradle 安装包。本书编写时 Gradle 的最新版本为 gradle-3.3,这里下载的就是这个版本,读者可以根据自己需要选择不同版本进行下载。下载后将 Gradle 文件解压到相应目录,这里将 Gradle 解压到 D:\software\gradle-3.3 目录下,安装及环境变量配置与 Scala 操作一样,新增系统环境 GRADLE_HOME
,指定 gradle 安装路径,并将;%GRADLE_HOME%\bin
添加到 path 中,这里依然是添加到 CLASSPATH 之中。
Gradle 安装及环境变量配置完成之后,打开 Windows 的 cmd 命令窗口,输入 gradle –version,若输出如图2-7所示信息,则表示 Gradle 安装配置成功。
图2-7 Gradle 安装验证结果
先进入这里下载 Kafka 源码文件。本书编写时 Kafka 的最新版本为 kafka-0.10.1.1,这里我们下载的是 kafka-0.10.1.1-src.tgz,将下载的源码包放在 F:\kafka-0.10.1.1 目录下,解压后如图2-8所示。
图2-8 Kafka 源码解压后的文件目录
进入 kafka-0.10.1.1-src,Kafka 源码包括图2-9所示的目录及文件。
图2-9 Kafka 源码包括的目录及文件
Kafka 源码对应目录及文件说明如表2-4所示。
表2-4 Kafka 源码对应目录及文件说明
名 称 | 描 述 |
---|---|
bin | 包括 Windows 和 Linux 平台下 Kafka 相关操作的执行脚本,如启动和关闭 KafkaServer、创建主题、分区管理、模拟生产者和消费者基本操作的脚本等 |
clients | Kafka 客户端,包括 KafkaProducer 和 KafkaConsumer,用 Java 语言开发 |
config | Kafka 运行相关配置文件,如在启动代理时需要加载的 server.properties 文件 |
connect | 0.9版本之后新增加的特性,提供了 Kafka 与其他系统整合进行数据导入、导出操作的统一接口,为 Kafka 能够与其他系统整合构建可水平扩展、高可靠的数据流处理平台提供了一个简单模型,用Java语言开发 |
core | Kafka 的核心代码,包括消息协议定义、日志管理、各组件之间通信、安全协议等 |
docs | Kafka 官方网站相关文档 |
examples | Kafka 实例代码 |
streams | Kafka 0.10 版本之后增加的新特性,是一个用来构建流处理程序的库,用Java语言开发 |
tools | Kafka 提供的工具类,用于查看生产者性能、吞吐量等 |
tests | 系统测试脚本 |
由于在 Kafka 源代码的 gradle 子目录中没有 wrapper 类库,因此在 Kafka 根目录下执行 gradlew eclipse 命令时会报图2-10所示的错误。
图2-10 Kafka 源码编译出错信息
接下来安装 wrapper 类库。由于本地安装的 Scala 版本为2.11.8,在安装 wrapper 类库之前,先修改 Kafka 源码目录下的 gradle.properties 文件,将 Scala 版本设置为 2.11.8。gradle.properties 文件内容如图2-11所示。
图2-11 gradle.properties 文件内容
然后进入 Kafka 源码根目录下,执行 gradle wrapper 命令来下载 wrapper 包,如图2-12所示。
图2-12 wrapper 安装过程输出信息
在该命令执行过程中会下载相应的 jar 文件,待完成相应文件下载后,若在控制台打印输出“BUILD SUCCESSFUL”字样则表示安装 wrapper 类库成功。执行成功后会在 Kafka 源码的 gradle 目录下生成 wrapper 目录,如图2-13所示。
图2-13 wrapper 安装过程创建的 wrapper 目录
进入 wrapper 目录,在该目录下已创建了一个 gradle-wrapper.jar 文件,如图2-14所示。
图2-14 wrapper 安装过程生成的文件
最后在 Kafka 源码根目录执行 gradlew eclipse 命令,对 Kafka 源码进行编译。这个过程由于要下载一系列依赖包,因此有些耗时,若出现“BUILD SUCCESSFUL”字样,则表示编译完成,如图2-15所示。
图2-15 Kafka 源码成功编译输出日志信息
若读者在编译时输入 gradlew eclipse 命令后控制台打印日志输出:
Downloading https://services.gradle.org/distributions/gradle-3.3-bin.zip .........................
一直卡在下载 gradle-3.3-bin.zip 时,可通过下载工具先下载 gradle-3.3-bin.zip 文件,然后复制到 C:\Users\用户名.gradle\wrapper\dists\gradle-3.3-bin\37qejo6a26ua35lyn7h1u9v2n 目录下,接着再次运行 gradlew eclipse 命令进行编译。
通过前面的步骤已完成了 Kafka 源码的编译,现在介绍如何将 Kafka 源码导入 Eclipse。在 Eclipse 视图中选择“import”,在弹出对话框中选择“Existing Projects into Workspace”,指定 Kafka 源码路径,依次导入 Kafka 源码中的 core 和 client 工程。导入项目后,若 Eclipse 的编码方式不是 UTF-8,会有错误提示,读者在导入 Kafka 源码时要确保 Eclipse 已设置 workspase 的编码方式为 UTF-8,同时建议修改 Scala 使用的 JVM 版本为1.8,如图2-16所示。
Eclipse 工作空间环境配置完毕后,导入 Kafka 的 core 和 client 工程,如图2-17所示。
图2-16 Eclipse 设置工程 Scala 运行的 JVM 版本界面
图2-17 Kafka 源码导入 Eclipse 效果
若在 Eclipse 中看到 core 工程有错误提示信息,则在 core 工程上右键配置“build path”,在 Libraries 视图下可以看到缺失如图2-18所示的两个文件,这两个文件都是 core 工程测试代码所依赖的文件,并不影响 core 工程本身的运行。这里为了简单,直接将这两个文件从 Libraries 中移除。
图2-18 Kafka core 报错所缺失的文件
若直接运行 core 工程,kafka.kafka.scala 会报出如图2-19所示的错误信息。
图2-19 Eclipse 启动 Kafka 时在控制台输出的错误信息
图2-19所示的错误是由于 Kafka 启动时需要加载 server.properties 文件,用于初始化 KafkaServer,因此在运行 kafka.kafka.scala 启动 KafkaServer 时,需要指定一个配置文件。KafkaServer 初始化的配置这里暂不进行详细介绍,将穿插在第3章至第6章对 Kafka 相关知识的讲解中进行介绍。现在,在 Eclipse 中设置运行参数,指定 server.properties 文件路径,配置如图2-20所示。由于 Kafka 依赖 ZooKeeper,因此要保证在启动 KafkaServer 之前先启动 ZooKeeper。
图2-20 Eclipse 设置 Kafka 启动加载配置文件界面
为了在控制台输出启动日志,需要将 Kafka 源码 config 目录下的 log4j.properties 文件复制到 Eclipse core 工程 src/main/scala 目录下,运行 kafka.scala 启动 KafkaServer,Eclipse 控制台输出启动日志信息如图2-21所示。
图2-21 Eclipse 启动 KafkaServer 输出结果
图2-21所示的日志信息表明:Kafka 源码已成功在 Eclipse 中运行起来。接下来就可以调试 Kafka,深入了解 Kafka 运行机制了。
本章详细讲解了 Kafka 运行环境安装部署的步骤,包括在 Windows 操作系统、Linux 操作系统安装部署 Kafka,以及 Kafka 可视化管理工具的安装和 Kafka 源码的编译等。
阅读全文: http://gitbook.cn/gitchat/geekbook/5a3896efc5896e6e1cf14f19