沉淀再出发:kafka初探
沉淀再出发:kafka初探
一、前言
从我们接触大数据开始,可能绕在耳边的词汇里面出现的次数越来越多的就包括kfaka了。kafka的设计初衷是希望作为一个统一的信息收集平台,能够实时的收集反馈信息,并需要能够支撑较大的数据量,且具备良好的容错能力。kafka是一个分布式消息队列,具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息,消费者从队列里取消息进行业务逻辑。一般在架构设计中起到解耦、削峰、异步处理的作用。
二、kafka的使用
2.1、安装jre和zookeeper
在安装kafka之前我们需要安装jre和zookeeper,有了这两个环境,kafaka才能运行,对于java的安装,大家可以参考我以前的拙作,对于在Windows中安装zookeeper,我这里简单介绍一下。
我们从这个地方下载zookeeper,我们这里使用:http://mirrors.hust.edu.cn/apache/zookeeper/ ,最好下载稳定版本的。
之后我们将其解压到某个目录之下,进行相应的配置,就可以使用了,因此zookeeper也是开箱即用的。
将"zoo_sample.cfg"重命名为"zoo.cfg"并修改其中的内容,新建一个存储数据的文件夹,将路径进行修改:
这里我们要注意,如果我们的路径写成下面的样子,我们会发现运行之后,并不能正确的找到目录,而是在根目录下面创建了一个文件夹:
究其本质原因还是目录的写法有问题,系统把'\'当成了转义字符了!!!!!!
正确的写法应该是:
这样运行的时候就能正常找到目录了:
设置环境变量:
然后运行,可以发现已经成功启动了:
2.2、安装kfaka
首先我们在官网上下载kfaka,然后安装即可:
下载完成后,我们同样的解压,配置相应的参数,加入环境变量中:
打开server.properties,修改日志目录,我们自己创建一个文件夹即可:
对于连接,默认就是本地的:
同样加入环境变量中:
2.3、运行kfaka
E:\kfaka_setup\kafka_2.11-2.0.1>.\bin\windows\kafka-server-start.bat .\config\server.properties
注意这里在Windows中,需要从bin目录下的windows目录运行程序:
2.4、创建topic
新启cmd,输入如下命令:
1 kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic zyr_test
2.5、创建生产者(producer)和消费者(consumer)
生产者:
1 kafka-console-producer.bat --broker-list localhost:9092 --topic zyr_test
消费者:
1 kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic zyr_test --from-beginning
于是整个运行的流程是这样的:
其实这里面还有很多需要设置的东西,比如一些文件的配置等等,通过这些配置可以搭建我们的集群环境。
三、kafka的本质
3.1、那么什么是kafka?
Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
Kafka的特性:
1 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。 2 可扩展性:kafka集群支持热扩展 3 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失 4 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败) 5 高并发:支持数千个客户端同时读写
Kafka的使用场景:
1 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。 2 消息系统:解耦和生产者和消费者、缓存消息等。 3 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。 4 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。 5 流式处理:比如spark streaming和storm 6 事件源
kafka对外使用topic的概念,生产者往topic里写消息,消费者从读消息。为了做到水平扩展,一个topic实际是由多个partition组成的,遇到瓶颈时,可以通过增加partition的数量来进行横向扩容,单个parition内是保证消息有序,每新写一条消息,kafka就是在对应的文件append写,所以性能非常高。 Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉去指定Topic的消息,然后进行业务处理。图中有两个topic,topic 0有两个partition,topic 1有一个partition,三副本备份。关于broker、topics、partitions的一些元信息用zk来存,监控和路由也都会用到zk。
3.2、专有名词的含义
1 1.producer: 2 消息生产者,发布消息到 kafka 集群的终端或服务。 3 2.broker: 4 kafka 集群中包含的服务器。 5 3.topic: 6 每条发布到 kafka 集群的消息属于的类别,即 kafka 是面向 topic 的。 7 4.partition: 8 partition 是物理上的概念,每个 topic 包含一个或多个 partition。kafka 分配的单位是 partition。 9 5.consumer: 10 从 kafka 集群中消费消息的终端或服务。 11 6.Consumer group: 12 high-level consumer API 中,每个 consumer 都属于一个 consumer group,每条消息只能被 consumer group 中的一个 Consumer 消费,但可以被多个 consumer group 消费。 13 7.replica: 14 partition 的副本,保障 partition 的高可用。 15 8.leader: 16 replica 中的一个角色, producer 和 consumer 只跟 leader 交互。 17 9.follower: 18 replica 中的一个角色,从 leader 中复制数据。 19 10.controller: 20 kafka 集群中的其中一个服务器,用来进行 leader election 以及 各种 failover。 21 12.zookeeper: 22 kafka 通过 zookeeper 来存储集群的 meta 信息。
3.3、生产者
创建一条记录,记录中一个要指定对应的topic和value,key和partition可选。 先序列化,然后按照topic和partition放进对应的发送队列中。kafka produce都是批量请求,会积攒一批,然后一起发送,不是调send()就进行立刻进行网络发包。
如果partition没填,key有填:按照key进行哈希,相同key去一个partition。(如果扩展了partition的数量那么就不能保证了);
如果partition没填,key没填:round-robin来选partition;
这些要发往同一个partition的请求按照配置,攒一波,然后由一个单独的线程一次性发过去。
当存在多副本的情况下,会尽量把多个副本,分配到不同的broker上。kafka会为partition选出一个leader,之后所有该partition的请求,实际操作的都是leader,然后再同步到其他的follower。当一个broker挂掉后,所有leader在该broker上的partition都会重新选举,选出一个leader,这里不像分布式文件存储系统那样会自动进行复制保持副本数。
然后这里涉及到两个细节:怎么分配partition,怎么选leader。关于partition的分配,还有leader的选举,总得有个执行者。在kafka中,这个执行者就叫controller。kafka使用zk在broker中选出一个controller,用于partition分配和leader选举。
partition的分配:
1 将所有Broker(假设共n个Broker)和待分配的Partition排序 2 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader) 3 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
1 1. producer 先从 zookeeper 的 "/brokers/.../state" 节点找到该 partition 的 leader 2 2. producer 将消息发送给该 leader 3 3. leader 将消息写入本地 log 4 4. followers 从 leader pull 消息,写入本地 log 后 leader 发送 ACK 5 5. leader 收到所有 ISR 中的 replica 的 ACK 后,增加 HW(high watermark,最后 commit 的 offset) 并向 producer 发送 ACK
3.4、broker 保存消息
存储策略:无论消息是否被消费,kafka 都会保留所有消息。有两种策略可以删除旧数据:
1. 基于时间:log.retention.hours=168
2. 基于大小:log.retention.bytes=1073741824
需要注意的是,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。
创建 topic
1 1. controller 在 ZooKeeper 的 /brokers/topics 节点上注册 watcher,当 topic 被创建,则 controller 会通过 watch 得到该 topic 的 partition/replica 分配。 2 2. controller从 /brokers/ids 读取当前所有可用的 broker 列表,对于 set_p 中的每一个 partition: 3 2.1 从分配给该 partition 的所有 replica(称为AR)中任选一个可用的 broker 作为新的 leader,并将AR设置为新的 ISR 4 2.2 将新的 leader 和 ISR 写入 /brokers/topics/[topic]/partitions/[partition]/state 5 3. controller 通过 RPC 向相关的 broker 发送 LeaderAndISRRequest
删除 topic:
1. controller 在 zooKeeper 的 /brokers/topics 节点上注册 watcher,当 topic 被删除,则 controller 会通过 watch 得到该 topic 的 partition/replica 分配。 2. 若 delete.topic.enable=false,结束;否则 controller 注册在 /admin/delete_topics 上的 watch 被 fire,controller 通过回调向对应的 broker 发送 StopReplicaRequest。
3.5、消费者
订阅topic是以一个消费组来订阅的,一个消费组里面可以有多个消费者。同一个消费组中的两个消费者,不会同时消费一个partition。换句话来说,就是一个partition,只能被消费组里的一个消费者消费,但是可以同时被多个消费组消费。因此,如果消费组内的消费者如果比partition多的话,那么就会有个别消费者一直空闲。
consumer API
kafka 提供了两套 consumer API:
1. The high-level Consumer API 2. The SimpleConsumer API
其中 high-level consumer API 提供了一个从 kafka 消费数据的高层抽象,而 SimpleConsumer API 则需要开发人员更多地关注细节。high-level consumer API 提供了 consumer group 的语义,一个消息只能被 group 内的一个 consumer 所消费,且 consumer 消费消息时不关注 offset,最后一个 offset 由 zookeeper 保存。使用 high-level consumer API 可以是多线程的应用,应当注意:
1. 如果消费线程大于 patition 数量,则有些线程将收不到消息 2. 如果 patition 数量大于线程数,则有些线程多收到多个 patition 的消息 3. 如果一个线程消费多个 patition,则无法保证你收到的消息的顺序,而一个 patition 内的消息是有序的
The SimpleConsumer API:如果想要对 patition 有更多的控制权,那就应该使用 SimpleConsumer API,比如:
1. 多次读取一个消息 2. 只消费一个 patition 中的部分消息 3. 使用事务来保证一个消息仅被消费一次
但是使用此 API 时,partition、offset、broker、leader 等不再透明,需要自己去管理。需要做大量的额外工作:
1. 必须在应用程序中跟踪 offset,从而确定下一条应该消费哪条消息 2. 应用程序需要通过程序获知每个 Partition 的 leader 是谁 3. 需要处理 leader 的变更
使用 SimpleConsumer API 的一般流程如下:
1. 查找到一个“活着”的 broker,并且找出每个 partition 的 leader 2. 找出每个 partition 的 follower 3. 定义好请求,该请求应该能描述应用程序需要哪些数据 4. fetch 数据 5. 识别 leader 的变化,并对之作出必要的响应
consumer group
kafka 的分配单位是 patition。每个 consumer 都属于一个 group,一个 partition 只能被同一个 group 内的一个 consumer 所消费(也就保障了一个消息只能被 group 内的一个 consuemr 所消费),但是多个 group 可以同时消费这个 partition。kafka 的设计目标之一就是同时实现离线处理和实时处理,根据这一特性,可以使用 spark/Storm 这些实时处理系统对消息在线处理,同时使用 Hadoop 批处理系统进行离线处理,还可以将数据备份到另一个数据中心,只需要保证这三者属于不同的 consumer group。
消费方式
consumer 采用 pull 模式从 broker 中读取数据。push 模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。对于 Kafka 而言,pull 模式更合适,它可简化 broker 的设计,consumer 可自主控制消费消息的速率,同时 consumer 可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。
consumer delivery guarantee
如果将 consumer 设置为 autocommit,consumer 一旦读到数据立即自动 commit。如果只讨论这一读取消息的过程,那 Kafka 确保了 Exactly once。但实际使用中应用程序并非在 consumer 读取完数据就结束了,而是要进行进一步处理,而数据处理与 commit 的顺序在很大程度上决定了consumer delivery guarantee:
1 1.读完消息先 commit 再处理消息。这种模式下,如果 consumer 在 commit 后还没来得及处理消息就 crash 了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于 At most once 2 2.读完消息先处理再 commit。 这种模式下,如果在处理完消息之后 commit 之前 consumer crash 了,下次重新开始工作时还会处理刚刚未 commit 的消息,实际上该消息已经被处理过了。这就对应于 At least once。 3 3.如果一定要做到 Exactly once,就需要协调 offset 和实际操作的输出。
经典的做法是引入两阶段提交。如果能让 offset 和操作输入存在同一个地方,会更简洁和通用。这种方式可能更好,因为许多输出系统可能不支持两阶段提交。比如,consumer 拿到数据后可能把数据放到 HDFS,如果把最新的 offset 和数据本身一起写到 HDFS,那就可以保证数据的输出和 offset 的更新要么都完成,要么都不完成,间接实现 Exactly once。(目前就 high-level API而言,offset 是存于Zookeeper 中的,无法存于HDFS,而SimpleConsuemr API的 offset 是由自己去维护的,可以将之存于 HDFS 中)总之,Kafka 默认保证 At least once,并且允许通过设置 producer 异步提交来实现 At most once。而 Exactly once 要求与外部存储系统协作,幸运的是 kafka 提供的 offset 可以非常直接非常容易得使用这种方式。
consumer rebalance
当有 consumer 加入或退出、以及 partition 的改变(如 broker 加入或退出)时会触发 rebalance。consumer rebalance算法如下:
1. 将目标 topic 下的所有 partirtion 排序,存于PT 2. 对某 consumer group 下所有 consumer 排序,存于 CG,第 i 个consumer 记为 Ci 3. N=size(PT)/size(CG),向上取整 4. 解除 Ci 对原来分配的 partition 的消费权(i从0开始) 5. 将第i*N到(i+1)*N-1个 partition 分配给 Ci
在 0.8.*版本,每个 consumer 都只负责调整自己所消费的 partition,为了保证整个consumer group 的一致性,当一个 consumer 触发了 rebalance 时,该 consumer group 内的其它所有其它 consumer 也应该同时触发 rebalance。这会导致以下几个问题:
1.Herd effect:任何 broker 或者 consumer 的增减都会触发所有的 consumer 的 rebalance 2.Split Brain:每个 consumer 分别单独通过 zookeeper 判断哪些 broker 和 consumer 宕机了,那么不同 consumer 在同一时刻从 zookeeper 看到的 view 就可能不一样,这是由 zookeeper 的特性决定的,这就会造成不正确的 reblance 尝试。 3. 调整结果不可控:所有的 consumer 都并不知道其它 consumer 的 rebalance 是否成功,这可能会导致 kafka 工作在一个不正确的状态。
基于以上问题,kafka 设计者考虑在0.9.*版本开始使用中心 coordinator 来控制 consumer rebalance,然后又从简便性和验证要求两方面考虑,计划在 consumer 客户端实现分配方案。
3.6、消息投递语义
kafka支持3种消息投递语义:
1 At most once:最多一次,消息可能会丢失,但不会重复 2 先获取数据,再commit offset,最后进行业务处理。 3 1、生产者生产消息异常,不管,生产下一个消息,消息就丢了 4 2、消费者处理消息,先更新offset,再做业务处理,做业务处理失败,消费者重启,消息就丢了 5 6 At least once:最少一次,消息不会丢失,可能会重复 7 先获取数据,再进行业务处理,业务处理成功后commit offset。 8 1、生产者生产消息异常,消息是否成功写入不确定,重做,可能写入重复的消息 9 2、消费者处理消息,业务处理成功后,更新offset失败,消费者重启的话,会重复消费 10 11 Exactly once:只且一次,消息不丢失不重复,只且消费一次(0.11中实现,仅限于下游也是kafka) 12 首先要保证消息不丢,再去保证不重复。 13 生产者重做导致重复写入消息----生产保证幂等性 14 消费者重复消费---消灭重复消费,或者业务接口保证幂等性重复消费也没问题 15 由于业务接口是否幂等,不是kafka能保证的,所以kafka这里提供的exactly once是有限制的,消费者的下游也必须是kafka。
四、总结
通过对kfaka的学习,我们更加的明白了生产者和消费者这个模型,也就是发布订阅模型,对我们以后的学习和使用有着重要的意义。