什么是Kafka?
1 kafka 是什么
Apache kafka is a distributed streaming platform,即官方定义 kafka 是一个分布式流式计算平台。而在大部分企业开发人员中,都是把 kafka 当成消息系统使用,即它是一个分布式消息队列,很少会使用 kafka 的流式计算。它有四个关键概念:
topic
kafka 把收到的消息按 topic 进行分类,因此可以理解为 topic 是一种类别
producer
往 kafka 发送消息的用户
consumer
接收 kafka 消息的用户
borker
kafka 集群可以由多个 kafka 实例组成,每个实例(server)称为 broker
无论是 kafka broker 本身,还是 producer 或者 consumer,都依赖于 zookeeper 集群保存一些 meta 信息,保证系统可用性,以及使用 zookeeper 的选举机制。
2 消息队列实现原理
点对点模式
一对一,消费者主动拉取数据,消息收到后消息清除。点对点模式通常是一个基于拉取或轮询的消息发送模型。此模型中,消费者从队列主动拉取信息,而不是消息系统推送消息给消费者,并且,消息只能被一个且只有一个消费者接收处理,即使有多个消息监听者也是如此。
发布订阅模式
一对多,数据生产后,推送给所有订阅者。发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态。
3 消息队列的作用
解耦
服务与服务之间不需要紧密结合,可以独立扩展,只要确保它们遵守一致的数据结构定义。也可以作为一种异步的实现方式。
冗余
消息队列把数据持久化直到它明确被处理,通过这一方式规避了数据丢失的风险。许多消息队列采用的“插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃
顺序保证
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
4 kafka 架构
1)producer:消息生产者,就是向kafka broker发消息的客户端
2)consumer:消息消费者,就是向 kafka broker 获取消息的客户端
3)topic:消息类别,也可以理解为一个队列
4)consumer group(CG):这是 kafka 用来实现一个 topic 下的消息的广播语义(发给所有的 consumer)和单播语义(发给任意一个 consumer)的手段。一个 topic 可以有多个 consumer group。topic 下的消息会复制给 consumer。**如果需要实现广播,那么只要每个 consumer 有一个独立的 consumer group。要实现单播,只要所有的 consumer 在同一个 consumer group。**consumer group 还可以将 consumer 进行自由的分组,而不需要多次发送消息到不同的 topic。
5)broker:一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个broker 可以容纳多个 topic。
6)partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的 id(offset)。kafka 只保证按一个 partition 中的顺序将消息发给 consumer,不保证一个 topic 的整体(多个partition间)的顺序。
7)offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafk a的文件即可。当然t he first offset 就是00000000000.kafka。
5 分布式模型
Kafka 每个主题的多个分区日志分布式地存储在 Kafka 集群上,同时为了故障容错,每个分区都会以副本的方式复制到多个消息代理节点上。其中一个节点会作为主副本(Leader),其他节点作为备份副本(Follower,也叫作从副本)。主副本会负责所有的客户端读写操作,备份副本仅仅从主副本同步数据。当主副本出现故障时,备份副本中的一个副本会被选择为新的主副本。因为每个分区的副本中只有主副本接受读写,所以每个服务器端都会作为某些分区的主副本,以及另外一些分区的备份副本,这样 Kafka 集群的所有服务端整体上对客户端是负载均衡的。
Kafka 的生产者和消费者相对于服务器端而言都是客户端。
Kafka 生产者客户端发布消息到服务端的指定主题,会指定消息所属的分区。生产者发布消息时根据消息是否有键,采用不同的分区策略。消息没有键时,通过轮询方式进行客户端负载均衡;消息有键时,根据分区语义(例如 hash)确保相同键的消息总是发送到同一分区。
Kafka 的消费者通过订阅主题来消费消息,并且每个消费者都会设置一个消费组名称。因为生产者发布到主题的每一条消息都只会发送给消费者组的一个消费者。所以,如果要实现传统消息系统的“队列”模型,可以让每个消费者都拥有相同的消费组名称,这样消息就会负责均衡到所有的消费者;如果要实现“发布-订阅”模型,则每个消费者的消费者组名称都不相同,这样每条消息就会广播给所有的消费者。
分区是消费者现场模型的最小并行单位。如下图(图1)所示,生产者发布消息到一台服务器的3个分区时,只有一个消费者消费所有的3个分区。在下图(图2)中,3个分区分布在3台服务器上,同时有3个消费者分别消费不同的分区。假设每个服务器的吞吐量时300MB,在下图(图1)中分摊到每个分区只有100MB,而在下图(图2)中,集群整体的吞吐量有900MB。可以看到,增加服务器节点会提升集群的性能,增加消费者数量会提升处理性能。
同一个消费组下多个消费者互相协调消费工作,Kafka 会将所有的分区平均地分配给所有的消费者实例,这样每个消费者都可以分配到数量均等的分区。Kafka 的消费组管理协议会动态地维护消费组的成员列表,当一个新消费者加入消费者组,或者有消费者离开消费组,都会触发再平衡操作。
Kafka 的消费者消费消息时,只保证在一个分区内的消息的完全有序性,并不保证同一个主题汇中多个分区的消息顺序。而且,消费者读取一个分区消息的顺序和生产者写入到这个分区的顺序是一致的。比如,生产者写入 “hello” 和 “Kafka” 两条消息到分区 P1,则消费者读取到的顺序也一定是 “hello” 和 “Kafka”。如果业务上需要保证所有消息完全一致,只能通过设置一个分区完成,但这种做法的缺点是最多只能有一个消费者进行消费。一般来说,只需要保证每个分区的有序性,再对消息假设键来保证相同键的所有消息落入同一分区,就可以满足绝大多数的应用
1.1 Kafka的特性:
- 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。
- 可扩展性:kafka集群支持热扩展
- 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
- 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
- 高并发:支持数千个客户端同时读写
1.2 Kafka的使用场景:
- 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
- 消息系统:解耦和生产者和消费者、缓存消息等。
- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
- 流式处理:比如spark streaming和storm
3.kafka安装配置
3.1下载地址
http://kafka.apache.org/downloads.html
-
-
创建 TOPIC:使用
kafka-topics.sh
创建单分区单副本的 topic test:bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
查看 topic 列表:
bin/kafka-topics.sh --list --zookeeper localhost:2181
-
产生消息:使用
kafka-console-producer.sh
发送消息:bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
向test发送一条包含key的消息:echo '00000,{"name":"Steve", "title":"Captain America"}' | kafka-console-producer.sh --broker-list localhost:9092 --topic test --property parse.key=true --property key.separator=,向test发送一条消息:echo "The first record" | kafka-console-producer.sh --broker-list localhost:9092 --topic test
-
消费消息:使用
kafka-console-consumer.sh
接收消息并在终端打印:bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning
或者bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
将消息的key也输出kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning --property print.key=true --property key.separator=, - 查看consumer group列表,使用--list参数:bin/kafka-consumer-groups.sh --bootstrap-server host:9092 --list
查看特定consumer group 详情,使用--group与--describe参数
bin/kafka-consumer-groups.sh --bootstrap-server HOST:9292 --group YOUR_GROUP_ID --describe -
看指定topic上每个partition的offset,是用于查看指定topic上相应分区的消息数,并不是consumer消费的偏移量
bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list host:9092 --topic topic
-
查看描述 TOPICS 信息
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test
在/etc/hosts里添加下hostname和127.0.0.1的映射就可以了:比如 127.0.0.1 localhost3、错误处理:Caused by: java.net.UnknownHostException: hostname: unknown er
启动时错误:[2018-05-14 21:40:33,305] ERROR There was an error in one of the threads during logs loading: kafka.common.KafkaException: Found directory /home/admin/kafka_2.12-1.1.0/bin, 'bin' is not in the form of topic-partition or topic-partition.uniqueId-delete (if marked for deletion).
错误原因:server.properties里的配置log.dir路径不对
4、认证和加密
使用ssl加密和认证,这样使用kafka的时候,必须使用证书进行连接
https://blog.csdn.net/Regan_Hoo/article/details/78770058
https://apereo.github.io/cas/4.0.x/installation/JAAS-Authentication.html#jaas-configuration-file
python客户端使用方法
1、首先安装客户端:pip install kafka-python [--user] :https://github.com/dpkp/kafka-python
2、注意mac和部分linux使用kafka-pyhton,需要指定api_version,参考:https://github.com/dpkp/kafka-python/issues/1308 和 https://github.com/dpkp/kafka-python/pull/1411,正确用法:api_version可以先不指定,不行的话再指定
#! /usr/bin/env python import time
from kafka import KafkaProducer
producer=KafkaProducer(bootstrap_servers="10.5.9.6:9092")
i=0
while True:
ts = int(time.time()*1000)
producer.send("test",value=str(i),key=str(i),timestamp_ms=ts,partition= 0
)#第1个参数为 topic名称,必须指定
#key : 键,必须是字节字符串,可以不指定(但key和value必须指定1个),默认为None
#value : 值,必须是字节字符串,可以不指定(但key和value必须指定1个),默认为None
#partition : 指定发送的partition,由于kafka默认配置1个partition,固为0
producer.flush()
print i
i=i+1
time.sleep(1)#! /usr/bin/env python from kafka import KafkaConsumer
consumer=KafkaConsumer("test",bootstrap_servers=["10.5.9.6:9092"])# consumer=KafkaConsumer("test",group_id='test_group',bootstrap_servers=["10.5.9.6:9092"],
consumer_timeout_ms=1000
) # 为topic:test创建group:test_group
# topic可以写到KafkaConsumer的参数里,也可以是下面的写法。group_id:指定此消费者实例属于的组名;若不指定 consumer_timeout_ms,默认一直循环等待接收,若指定,则超时返回,不再等待.consumer_timeout_ms : 毫秒数#
consumer.subscribe(pattern= '^my.*') 使用正则表达式订阅多个topic
#consumer.subscribe(topics= ['my_topic', 'topic_1']) 订阅多个topic
for message in consumer:
print message注意config/config/server.properties,一定要设置为本机的大网IP,不然其他机器的producer和consumer无法访问这个机器的broker,参见参考4的说明
Kafka架构
术语:
- Broker
Kafka集群包含一个或多个服务器,这种服务器被称为broker。可以在一个机器上部署多个broker,也可以在不同的机器上部署多个broker。物理概念,指服务于Kafka的一个node。
- Topic:可以理解为是一个queue序列
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic 的 消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
- Partition
Parition是物理上的概念,每个Topic包含一个或多个Partition。是Kafka下数据存储的基本单元。同一个topic的数据,会被分散的存储到多个partition中,这些partition可以在同一台机器上,也可以是在多台机器上。为了做到均匀分布,通常partition的数量通常是Broker Server数量的整数倍。
- Producer
负责发布消息到Kafka broker
- Consumer
消息消费者,向Kafka broker读取消息的客户端。
- Consumer Group
每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则会创建默认的group)。同样是逻辑上的概念,是Kafka实现单播和广播两种消息模型的手段。同一个topic的数据,会广播给不同的group;同一个group中的worker,只有一个worker能拿到这个数据。换句话说,对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个worker消费。group内的worker可以使用多线程或多进程来实现,也可以将进程分散在多台机器上,worker的数量通常不超过partition的数量,且二者最好保持整数倍关系,因为Kafka在设计时假定了一个partition只能被一个worker消费(同一group内)。
为了便于实现MQ中的多播,重复消费等引入的概念。如果ConsumerA以及ConsumerB同在一个UserGroup,那么ConsumerA消费的数据ConsumerB就无法消费了。
即:所有usergroup中的consumer使用一套offset。
- 什么是消费者组(Consumer Group)
consumer group是kafka提供的可扩展且具有容错性的消费者机制。既然是一个组,那么组内必然可以有多个消费者或消费者实例(consumer instance),它们共享一个公共的ID,即group ID。组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。理解consumer group记住下面这三个特性就好了:
1)consumer group下可以有一个或多个consumer instance,consumer instance可以是一个进程,也可以是一个线程
2)group.id是一个字符串,唯一标识一个consumer group
3)consumer group下订阅的topic下的每个分区只能分配给某个group下的一个consumer(当然该分区还可以被分配给其他group) -
Offset
Offset专指Partition以及User Group而言,记录某个user group在某个partiton中当前已经消费到达的位置。
- 查看group信息:test1234、test_group是主动创建的
./bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
- group如何创建?cousumer消费时,为topic指定group名字,就会创建相应的group
- 一个topic可以同属于多个group,topic会广播给所有包含它的group
- 一个group可以包含多个topic,这个group会收到所有它包含的topic的信息
- 属于同一个group的多个客户端(或者线程、进程),其中只有一个可以收到相同topic的信息,其他收不到
- 默认从上次的最后一次消费继续消费(这样保证不会重复消费),也可以通过设置从头开始消费
- 一个topic中partition的数量,就是每个user group中消费该topic的最大并行度数量。
Python Kafka的几个客户端对比基准测试:https://www.ctolib.com/topics-103354.html
kafka-python文档地址:https://kafka-python.readthedocs.io/en/master/apidoc/KafkaConsumer.html
使用参考:https://zhuanlan.zhihu.com/p/38330574
更多操作kafka的python三方包比较参考:https://github.com/muscledreamer/Kafka_Demo
查看Topic的分区和副本情况
命令:
bin/kafka-topics.sh --describe --zookeeper 127.0.0.1:2181 --topic test0
运行结果:
Topic:test0 PartitionCount:16 ReplicationFactor:3 Configs:
Topic: test0 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 1,0,2
Topic: test0 Partition: 1 Leader: 1 Replicas: 1,2,0 Isr: 1,0,2
Topic: test0 Partition: 2 Leader: 2 Replicas: 2,0,1 Isr: 1,0,2
Topic: test0 Partition: 3 Leader: 1 Replicas: 1,2,0 Isr: 1,0,2
Topic: test0 Partition: 4 Leader: 2 Replicas: 2,0,1 Isr: 1,0,2
Topic: test0 Partition: 5 Leader: 0 Replicas: 0,1,2 Isr: 1,0,2
Topic: test0 Partition: 6 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 7 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 8 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic: test0 Partition: 9 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic: test0 Partition: 10 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 11 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 12 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic: test0 Partition: 13 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 14 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 15 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
结果分析:
第一行显示partitions的概况,列出了Topic名字,partition总数,存储这些partition的broker数
以下每一行都是其中一个partition的详细信息:
leader
是该partitons所在的所有broker中担任leader的broker id,每个broker都有可能成为leader
replicas
显示该partiton所有副本所在的broker列表,包括leader,不管该broker是否是存活,不管是否和leader保持了同步。
isr
in-sync replicas的简写,表示存活且副本都已同步的的broker集合,是replicas的子集
举例:
比如上面结果的第一行:Topic: test0 Partition:0 Leader: 0 Replicas: 0,2,1 Isr: 1,0,2
Partition: 0
该partition编号是0
Replicas: 0,2,1
代表partition0 在broker0,broker1,broker2上保存了副本
Isr: 1,0,2
代表broker0,broker1,broker2都存活而且目前都和leader保持同步
Leader: 0
代表保存在broker0,broker1,broker2上的这三个副本中,leader是broker0
leader负责读写,broker1、broker2负责从broker0同步信息,平时没他俩什么事
当producer发送一个消息时,producer自己会判断发送到哪个partiton上,如果发到了partition0上,消息会发到leader,也就是broker0上,broker0处理这个消息,broker1、broker2从broker0同步这个消息
如果这个broker0挂了,那么kafka会在Isr列表里剩下的broker1、broker2中选一个新的leader -