Kafka学习入门
最近工作中用到了两个很给力的项目,一个是Kafka,一个是Strom。本着自我学习并方便他人的目的,我会将我觉得比较有用的英文文档翻译在此(保留系统专有名词不作翻译)。
1kafka介绍
Kafka是一个分布式的(distributed),可划分的(partitioned),冗余备份的持久性的日志服务(replicated commit log service). 提供消息系统(messaging system)的功能,拥有独一无二的设计。
这是什么意思呢?
首先,让我们看一下“消息(messaging)”的基本术语:
-
topics
: (翻译过来叫话题)特指Kafka处理的消息源(feeds of messages)的不同分类。 【注:feeds翻译为源,饲料,在计算机领域,特指一种用户从数据源接收消息的机制(见Wikipedia),比如RSS feeds等。这里指接收messages.】 -
producers
:向Kafka的一个topic发布消息的过程叫做producers。就是向kafka broker发消息的客户端。 -
consumers
:订阅topics并处理其发布的消息的过程叫做consumers。就是向kafka broker取消息的客户端。 -
broker
:Kafa集群中的一台或多台服务器统称为broker。
所以,从总体来说,produces
通过网络向Kafka的集群发送消息并转由consumers
来处理。这个过程如下图所示:
客户端和服务器端的通信,是基于简单,高性能,且与编程语言无关的TCP协议。虽然我们为Kafka提供了Java版本的客户端,但是客户端其实可以使用多种语言。
2.Topics与日志
让我们首先来研究一下Kafka提供的这个抽象的概念 – topic
一个topic
是指发布的消息的一个类别或消息源的名字。Kafka集群将为每一个topic
划分一个日志(partitioned log)如下图所示:
每一个partition是一个排好序的,不可变的消息序列。新的消息不断的追加到序列的尾部。–即持久性日志(commit log)。每一个分区(partition
)中的消息,有一个名叫offset
的顺序编号,作为这条消息在partition
中的标识。
Kafka集群将在设定的时间范围内,保存所有被发布的消息,不论该消息是否被处理完成。例如,如果一个日志被设置为保存2天,那么在它发布的两天之内,它都是可以被处理的,而在2天之后,它就会被系统销毁并释放掉。
Kafka的性能与数据的大小之间是常数的关系,所以保存大量的数据是没有问题的。
实际上,每个consumer
中仅有的元数据(metadata)的主要部分是该consumer
在日志中的位置信息,叫做offset
(偏移量)。这个offset
由consumer
控制,在一般情况下,consumer
按照offset
的顺序读取消息,但事实上consumer
可以控制位置,可以以任何想要的顺序处理消息。例如一个consumer
可以重置并重新处理一个已经处理过的offset
。
这些特点的组合使Kafka的consumers
变得特别的廉价–它们能来去自如而不会对集群或者其它的consumers
造成多大影响。比如,你可以使用我们的命令行工具来“tail”任意topic
中的内容,而不会改变任何被已有consumers
处理过的内容。
日志服务的分区有几个目的。首先,它允许日志扩展到超过单台服务器允许的大小。因为虽然每一个单独的分区必须适应承载它们的服务器,但是一个topic
可以包含多个分区,所以能处理任意大小的数据。其次,它们作为并行单元–一会儿我们会了解更多。
3.分布式
日志的分区partitions
分布式地部署在Kafka服务器集群上,每个服务器为一个共享的分区处理数据和请求。每一个分区的数据被冗余的备份在多台服务器上用于容错,可以通过配置来设定用于备份的服务器的数量。
每个分区有一台服务器扮演“领导”的角色,有0或多台服务器扮演“随从”。领导负责所有对分区的读写请求,同时随从们被作为领导的备份。一旦领导挂掉,随从中的一个会自动变成新的领导。每一台服务器都同时在一些分区里扮演领导而在另外一些分区中充当随从,这让集群拥有良好的负载均衡。
4.Kafka消息有序性
5.Producers (生产者)
producers
向他们选择的topics
发布数据。每个producer
负责在topic
中选择将哪些消息分配给哪些分区。最简单的方式从分区列表中轮流选择。也可以根据某种算法依照权重选择分区例如通过简单的“循环赛”的方式来或是根据一些语义划分的方法(比如根据一些消息中的键)来实现负载均衡。开发者负责如何选择分区的算法。
6.Consumers (消费者)
传统的消息系统有两种模式:消息队列和发布/订阅。在消息队列模式中,一池子的consumers可能从一个服务器上读取数据,每个消息被分发给其中一个consumer;而在发布订阅模式中,消息被以广播的方式发给所有的consumers。Kafka通过提供了一个叫做消费者群组(consumer group)的抽象概念涵盖以上两种模式。
Consumers
上标记有他们所属的consumer group的名字,每个被发布到topic
上的消息消息会被分发到所有订阅该topic
的consumer group内部的一个consumer实例上。Consumer实例可以是一个单独的进程也可以是一个单独的机器。
如果所有的consumer实例都具有相同的群组,那么就像传统的队列模式一样平衡着各个consumer的负载。
如果所有的consumer实例均有不同的群组,那么这就如同发布/订阅模式,所有的消息被广播给所有的消费者。
更常见的情况是,我们发现topics一般只有很少的消费者群组,一个群组一般对应一个“逻辑订阅”单元。而每一个群组由大量的consumer实例构成,用来提供可扩展性和容错性。这其实就是发布/订阅模式的一种特殊情况,只不过订阅者是一个consumers的集群而非一个单独的进程而已。
同时,Kafka具有比传统消息系统更强大的顺序保障。
传统的队列在服务器上按照一定的顺序存储消息,然后当多个consumers从队列中处理消息时,系统按照消息存储的顺序分发消息。然而,虽然系统是按照顺序送出消息的,但是是按异步的方式送达到consumer手中,所以当消息到达不同consumer手中的时候,已经没有顺序可言了。这意味着消息的顺序在并行处理中不复存在了。消息系统常常有一个权宜之计来应对这种情况,就是使用了一个叫做“独家消费”的概念,就是只允许一个进程处理队列,不过这么做的话,并行处理当然也就不复存在了。
Kafka在这一点上做的比较好。通过一个叫做“排比parallelismparallelism”的概念–即并行–在topics中, Kafka能够同时提供顺序保证和一池子消费进程间的负载均衡。
Kafka只能保证每个分区内部的消息的总体顺序,而保证同一个topic在不同分区中消息的顺序。这种每个分区有序并可以按照数据的键去分区的特性对于大多数应用都已经足够。但是,如果你需要保证所有消息的总体顺序,可以通过使用只有一个分区的topic去完成,不过这样做就意味着只有一个consumer进程了。
7.保障
在高层次上Kafka提供如下保障:
- 由producer发送给特点topic分区的消息按照发送的先后顺序排序。也就是说,如果同一个producer发送了消息M1和M2,M1先被发送,那么M1的offset比M2的小,且M1先出现在日志中。
- 一个consumer实例按照消息在日志中存储的顺序收到消息。
- 对于一个有N个备份的topic,我们允许其中N-1个服务器挂掉,依然能保证不丢失任何持久性日志中的消息。
8.用例
Kafka可以用于:
- 消息系统, 例如ActiveMQ 和 RabbitMQ.
- 站点的用户活动追踪。 用来记录用户的页面浏览,搜索,点击等。
- 操作审计。 用户/管理员的网站操作的监控。
- 日志聚合。收集数据,集中处理。
- 流处理。
- [Event sourcing] (http://martinfowler.com/eaaDev/EventSourcing.html)
- Commit Log
讲了Kafka的背景知识这么多,我们还是快点开始实践之旅吧。
假定你还没有任何的Kafka和Zookeeper环境。
8.1: 下载代码
下载 0.8.1 版本并解压。 (当前最新的稳定版本是0.8.1.1)
1
2
|
> tar -xzf kafka_2.9.2-0.8.1.1.tgz
> cd kafka_2.9.2-0.8.1.1
|
8.2: 启动服务
Kafka使用Zookeeper所以你可能先要安装一个ZooKeeper.你可以使用kafka中打包好的脚本或者一个配置好的Zookeeper.
1
2
3
|
> bin/zookeeper-server-start.sh config/zookeeper.properties
[2013-04-22 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
...
|
现在可以启动Kafka了:
1
2
3
4
|
> bin/kafka-server-start.sh config/server.properties
[2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties)
[2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties)
...
|
8.3: 新建一个话题Topic
Topic的名字叫"test",只有一个分区和一个备份。
1
|
> bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
|
查看存在的Topic列表:
1
2
|
> bin/kafka-topics.sh --list --zookeeper localhost:2181
test
|
除了手工创建Topic,你也可以配置你的broker当发布一个不存在的topic时自动创建topic。
8.4: 发送消息
Kafka提供了一个命令行的工具,可以从输入文件或者命令行中读取消息并发送给Kafka集群。每一行是一条消息。
1
2
3
|
> bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
This is a message
This is another message
|
8.5: 消费消息
Kafka也提供了一个消费消息的命令行工具。
1
2
3
|
> bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning
This is a message
This is another message
|
这些命令行工具有很多的选项,你可以查看他们的文档来了解更多的功能。
8.6: 设置多个broker
目前我们运行在一个broker,不好玩。
让我们来点大的。
首先为每个broker创建一个配置文件。
1
2
|
> cp config/server.properties config/server-1.properties
> cp config/server.properties config/server-2.properties
|
修改文件如下:
1
2
3
4
5
6
7
8
9
|
config/server-1.properties:
broker.id=1
port=9093
log.dir=/tmp/kafka-logs-1
config/server-2.properties:
broker.id=2
port=9094
log.dir=/tmp/kafka-logs-2
|
broker.id属性别重样。为了在一台机器上启动两个broker,改了一下它们的port的。
Zookeeper还在,上面用的broker还活着。 来启动这两个broker.
1
2
3
4
|
> bin/kafka-server-start.sh config/server-1.properties &
...
> bin/kafka-server-start.sh config/server-2.properties &
...
|
创建一个topic试试, 奢侈一把,把备份设置为3:
1
|
> bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic
|
成了。运行 "describe topics" 命令瞧瞧:
1
2
3
|
> bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs:
Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
|
第一行给出了分区的汇总信息。每个分区行给出分区信息。
"leader" 节点是1.
"replicas" 信息,在节点1,2,0上,不管node死活,只是列出信息而已.
"isr" 工作中的复制节点的集合. 也就是活的节点的集合.
来看看一开始创建的节点:
1
2
3
|
> bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test
Topic:test PartitionCount:1 ReplicationFactor:1 Configs:
Topic: test Partition: 0 Leader: 0 Replicas: 0 Isr: 0
|
毫无新意,想必你已经明了了。
发布个消息:
1
2
3
4
5
|
> bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic
...
my test message 1
my test message 2
^C
|
消费它:
1
2
3
4
5
|
> bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic
...
my test message 1
my test message 2
^C
|
测试一下容错. 干掉leader,也就是Broker 1:
1
2
3
|
> ps | grep server-1.properties
7564 ttys002 0:15.91 /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java...
> kill -9 7564
|
Leader被切换到一个follower上节, 点 1 不会被列在isr中了,因为它死了:
1
2
3
|
> bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs:
Topic: my-replicated-topic Partition: 0 Leader: 2 Replicas: 1,2,0 Isr: 2,0
|
但是,消息没丢啊,不信你试试:
1
2
3
4
5
|
> bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic
...
my test message 1
my test message 2
^C
|
8.7生产者的例子
8.8消费者的例子
参考:
https://www.cnblogs.com/intsmaze/p/6386616.html