Kafka 概述
Kafka
是一个分布式消息队列,用于构建实时数据管道和流处理。它具有横向扩展能力、容错能力,并且运行速度快,能够在上千台服务器上运行
Kafka的整体结构
Producer
:消息生产者,就是向kafka broker
发消息的客户端Consumer
:消息消费者,向kafka broker
取消息的客户端Topic
:主题,Kafka
根据主题对消息进行分类,在Kafka
中的每条消息都有Topic
。在我们使用时会将不同类型的数据设置为不同的主题Partition
:消息分区,Kafka
会将接收到的消息进行分区,一个topic
可以分为多个partition
,每个partition
是一个有序的队列。partition
中的每条消息都会被分配一个有序的id(offset)
。kafka
只保证按一个partition
中的顺序将消息发给consumer
,不保证多个partition
间的顺序Consumer Group (CG)
:这是kafka
用来实现topic
消息广播(发给所有的consumer
)的手段。一个topic
可以给多个CG
。topic
的消息会发送到到所有的CG
。用CG
还可以将consumer
进行自由的分组而不需要多次发送消息到不同的topic
Broker
:消息中间件处理节点,一台kafka
服务器就是一个broker
。一个集群由多个broker
组成。
broker
存储topic
的数据。如果某topic
有N
个partition
,集群有N
个broker
,那么每个broker
存储该topic
的一个partition
(避免broker
数少于N
)Replication
:Kafka
支持以Partition
为单位对Message
进行冗余备份,每个Partition
都可以配置至少 1 个Replication
(当仅 1 个Replication
时即仅该Partition
本身)Leader
:每个Replication
集合中的Partition
都会选出一个唯一的Leader
,所有的读写请求都由Leader
处理。其他Replicas
从Leader
处把数据更新同步到本地。每个Cluster
当中会选举出一个Broker
来担任Controller
,负责处理Partition
的Leader
选举,协调Partition
迁移等工作ISR(In-Sync Replica)
:是Replicas
的一个子集,表示目前Alive
且与Leader
能够Catch-up
的Replicas
集合。读写都是首先落到Leader
上
Kafka
的选举机制:
首先Kafka
会将接收到的消息分区(partition
),每个主题(topic
)的消息有不同的分区。
为了保证高可用,每个分区都会有一定数量的副本(replica
)。这样如果有部分服务器不可用,副本所在的服务器就会接替上来,保证应用的持续性。
为了保证较高的处理效率,消息的读写都是在固定的一个副本上完成。这个副本就是所谓的Leader
,而其他副本则是Follower
。而Follower
则会定期地到Leader
上同步数据。
Kakfa Broker
集群受Zookeeper
管理(为什么先安装Zookeeper
):
所有的Kafka Broker
节点一起去Zookeeper
上注册一个临时节点,因为只有一个Kafka Broker
会注册成功,其他的都会失败,
这个成功在Zookeeper
上注册临时节点的这个Kafka Broker
会成为Kafka Broker Controller
,其他的Kafka broker
叫Kafka Broker follower
。(这个过程叫Controller
在ZooKeeper
注册Watch
)。
一旦有一个broker
宕机了怎么办:
这个kafka broker controller
会读取该宕机broker
上所有的partition
在zookeeper
上的状态,并选取ISR
列表中的一个replica
作为partition leader
,这个broker
宕机的事情,kafka controller
也会通知zookeeper
,zookeeper
就会通知其他的kafka broker
Kafka 安装和使用
-
安装
Zookeeper
0.1 下载zookeeper
,配置ZK_HOME
0.2 进入conf
目录,创建配置文件cd cd /usr/local/zookeeper/conf/ cp zoo_sample.cfg zoo.cfg # conf目录下默认没有zoo.cfg,需要把zoo_sample.cfg改为zoo.cfg文件 # zk启动的时候会加载zoo.cfg 修改zoo.cfg 中的 dataDir=/xxx 项 # (默认为系统tmp目录下,重启后数据会丢失) 修改 server.A=B:C:D 项 # A代表这是第几号服务器;B是服务器的IP地址;C表示服务器与群集中的“领导者”交换信息的端口;当领导者失效后,D表示用来执行选举时服务器相互通信的端口。例: server.1=192.168.100.10:2888:3888 # 第一个服务器使用 server.2=192.168.100.11:2888:3888 # 第二个服务器使用 server.3=192.168.100.12:2888:3888 # 第三个服务器使用 # 每个集群机上 一个 server.A=B:C:D 项
0.3 启动
zookeeper
集群
在三个服务器上都执行:cd /usr/local/zookeeper/bin zkServer.sh start # 启动zookeeper的服务端
0.4 验证
./zkServer.sh status
0.5 测试连接服务端
zkCli.sh -server 192.168.100.10:2181#启动zookeeper的客户端
-
下载 Kafka
-
解压
tar -xzf kafka_2.12-2.4.1.tgz -C /安装路径 cd /安装路径 ln -s kafka_2.12-2.4.1/ kafka # 创建硬链接
-
修改配置文件
cd /config vim server.properties # 单节点单broker 设置 broker.id=0 在集群中需要设置为唯一的 log.dir=/日志路径/日志名 # 单节点多broker 设置 config/server-1.properties: broker.id=1 listeners=PLAINTEXT://:9093 log.dir=/日志路径/日志名-1 config/server-2.properties: broker.id=2 listeners=PLAINTEXT://:9094 log.dir=/日志路径/日志名-2 # zookeeper集群 另外需要修改 zookeeper.connect=192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181
-
启动
Kafka
自带的zookeeper
,以守护进程运行kafka安装路径/bin/zookeeper-server-start.sh -daemon kafka安装路径/config/zookeeper.properties
注意:若在
kafka 目录下
则不需要填写/kafka安装路径
-
现在启动
Kafka
服务器 (集群则每台服务器都启动后台启动)
/bin/kafka-server-start.sh -daemon /config/server.properties
-
创建
topic
创建一个单分区和三个副本创建topic
:./bin/kafka-topics.sh --create --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.2012:2181 --replication-factor 3 --partitions 1 --topic test
--zookeeper
:zk
集群服务器的IP:端口
--replication-factor
:副本数量--partitions
:分区数量--topic
:topic
的名称
现在,可以运行
list topic
命令,查看创建的所有topic
:bin/kafka-topics.sh --list --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181
除了手动创建
topic
外,发布不存在的topic
时也会自动创建topic
(在代理配置中设置) -
开启消费者与生产者(脚本模式开启)
开启消费者 /脚本路径/消费者脚本.sh --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181 --topic topic名 # 若指定的topic名不存在,则取不到数据 # --from-beginning:从最开始生产队的数据开始消费 开启生产者(--sync表示异步开启) /脚本路径/生产者者脚本.sh --broker-list 192.168.100.10:2181,192.168.100.11:2181,192.168.100.2012:2181 --sync --topic topic名 # 若指定的topic名不存在,则新建
Kafka python API
kafka-python 文档
下载安装kafka-python
: pip install kafka-python
验证是否安装成功:import kafka
创建生产者
三个基本属性:
bootstrap.servers
:属性值是一个host:port
的broker
列表。key.serializer
:因此需要将这些对象序列化成字节数组key.serializer
指定的类需要实现org.apache.kafka.common.serialization.Serializer
接口,Kafka
客户端包中包含了几个默认实现,例如ByteArraySerializer
、StringSerializer
和IntegerSerialier
value.serializer
:属性值是类的名称。这个属性指定了用来序列化消息记录的类,与key.serializer
差不多
-
发送普通字节数据的生产者
>>> from kafka import KafkaProducer # 导入KafkaProducer >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口') # 创建生产者实例 >>> for _ in range(100): ... producer.send('topic名', b'普通数据') # 循环发送100次字节数据
-
发送
json
字符串 的生产者# 建立KafkaProducer的实例 # value_serializer:将数据进行序列化的操作 # lambda v: json.dumps(v).encode('utf-8'):将传入的数据转换为json,并进行utf-8编码 >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口',value_serializer=lambda v: json.dumps(v).encode('utf-8')) #发送一个字典数据 >>> producer.send('topic名', {'key1': 'value1'}) <kafka.producer.future.FutureRecordMetadata object at 0x2a9ebd0> >>>
-
压缩式字节数据 的生产者
compression_type:压缩类型,'gzip' # 建立生产者和broker进程的连接,指定要锁方式为gzip >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口',compression_type='gzip') # 发送数据 >>> producer.send('topic名', b'要压缩的普通大数据')
-
发送 格式化
string keys
的生产者>>> producer = KafkaProducer(key_serializer=str.encode) >>> producer.send('topic名', key='ping', value=b'1234')
-
生产者的更多功能
# 阻塞,直到发送一条消息(或超时) future = producer.send('foobar', b'another_message') result = future.get(timeout=60) # 阻塞,直到所有未决消息至少都发送到网络上 # 请注意:这并不保证发送成功!只有在使用linger_ms配置内部批处理时才有用 producer.flush() # 包括 record headers 。格式是包含字符串键和字节值的元组列表 producer.send('topic',value = b'c29tZSB2YWx1ZQ ==',headers = [('content-encoding',b'base64')])) # 获取生产者绩效指标 metrics = producer.metrics()
创建消费者
-
创建普通消费者
>>> from kafka import KafkaConsumer # 导入KafkaConsumer >>> consumer = KafkaConsumer('topic名') # 创建消费者实例,并指定topic名 >>> for msg in consumer: # 从消息对列取数据 ... print (msg) # 访问 record headers。返回的值是一个带有字符串键和字节值的元组列表 >>> for msg in consumer: ... print (msg.headers) # 获取消费者指标 metrics= Consumer.metrics()
-
加入一个消费者组以进行 动态 分配
partition
和offset
>>> from kafka import KafkaConsumer >>> consumer = KafkaConsumer('topic名', group_id='my_favorite_group') >>> for msg in consumer: ... print (msg)
-
手动为消费者 分配
partition
列表>>> from kafka import TopicPartition >>> consumer = KafkaConsumer(bootstrap_servers='IP地址:端口') >>> consumer.assign([TopicPartition('topic名', 2)]) >>> msg = next(consumer)
-
反序列化
msgpack-encoded
的值>>> consumer = KafkaConsumer(value_deserializer=msgpack.loads) >>> consumer.subscribe(['msgpackfoo']) >>> for msg in consumer: ... assert isinstance(msg.value, dict)
Kafka-python Demo
目的:利用producer
将某个目录下的所有文件名发送到指定topic
,并由consumer
来接收
- 创建
producer
from kafka import KafkaProducer
import json
import os
import time
from sys import argv
# 建立Producer与Kafka进程的连接
producer = KafkaProducer(bootstrap_servers='node-teach:9092')
# 定义一个日志输出方法,进行日志格式化输出
def log(str):
t = time.strftime(r"%Y-%m-%d_%H-%M-%S",time.localtime())
print("[%s]%s"%(t,str))
# 查看目录下的所有文件并利用生产者进行数据生产。
def list_file(path):
dir_list = os.listdir(path);
for f in dir_list:
producer.send('test_topic',f.encode())
producer.flush()
log('send: %s' % (f))
list_file(argv[1])
# 关闭生产者的连接
producer.close()
log('done')
- 创建
consumer
from kafka import KafkaConsumer
import time
# 格式化输出日志
def log(str):
t = time.strftime(r"%Y-%m-%d_%H-%M-%S",time.localtime())
print("[%s]%s"%(t,str))
log('start consumer')
# 消费node-teach:9092上的test_topic 这个Topic
consumer=KafkaConsumer('test_topic',bootstrap_servers=['node-teach:9092'])
for msg in consumer:
recv = "%s:%d:%d: key=%s value=%s" %(msg.topic,msg.partition,msg.offset,msg.key,msg.value)
log(recv)
当前的consumer
只能接收自身启动后的数据,如果需要启动后将之前的数据全部接收,需要设置consumer
的group_id
,kafka
内部会将数据广播到分组上,组会将数据转接到对应的consumer
上。
consumer=KafkaConsumer('test_topic',group_id='test_group_id',bootstrap_servers=['node-teach:9092'])