python连接kafka的标准库,kafka-python和pykafka。kafka-python使用的人多是比较成熟的库,kafka-python并没有zk的支持。pykafka是Samsa的升级版本,使用samsa连接zookeeper,生产者直接连接kafka服务器列表,消费者才用zookeeper。
# PyPI安装
pip install kafka-python
# conda安装
conda install -c conda-forge kafka-python
# anaconda自带pip安装
/root/anaconda3/bin/pip install kafka-python
- 官网:https://kafka-python.readthedocs.io/en/master/index.html
- git:https://github.com/dpkp/kafka-python
注意:1.4.0 以上的 kafka-python 版本使用了独立的心跳线程去上报心跳
API:https://kafka-python.readthedocs.io/en/master/apidoc/KafkaProducer.html。
生产者代码是线程安全的,支持多线程,而消费者则不然。
class kafka.KafkaProducer(**configs)
send(topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None)
函数返回FutureRecordMetadata类型的RecordMetadata数据
flush(timeout=None)
发送所有可以立即获取的缓冲消息(即时linger_ms大于0),线程block直到这些记录发送完成。当一个线程等待flush调用完成而block时,其它线程可以继续发送消息。
注意:flush调用不保证记录发送成功
metrics(raw=False)
获取生产者性能指标。
#-*- encoding:utf-8 -*-
from kafka import KafkaProducer
import json
producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])
for i in range(0, 100):
producer.send('MY_TOPIC1', value=b'lai zi shouke de msg', key=None, headers=None, partition=None, timestamp_ms=None)
# Block直到单条消息发送完或者超时
future = producer.send('MY_TOPIC1', value=b'another msg',key=b'othermsg')
result = future.get(timeout=60)
print(result)
# future.get函数等待单条消息发送完成或超时,经测试,必须有这个函数,不然发送不出去,或用time.sleep代替,待验证
# Block直到所有阻塞的消息发送到网络
# 注意: 该操作不保证传输或者消息发送成功,仅在配置了linger_ms的情况下有用。(It is really only useful if you configure internal batching using linger_ms
# 序列化json数据
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092', value_serializer=lambda v: json.dumps(v).encode('utf-8'))
producer.send('MY_TOPIC1', {'shouke':'kafka'})
# 序列化字符串key
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092', key_serializer=str.encode)
producer.send('MY_TOPIC1', b'shouke', key='strKey')
# 压缩
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092',compression_type='gzip')
for i in range(2):
producer.send('MY_TOPIC1', ('msg %d' % i).encode('utf-8'))
# 消息记录携带header
producer.send('MY_TOPIC1', value=b'c29tZSB2YWx1ZQ==', headers=[('content-encoding', b'base64'),])
# 获取性能数据(注意,实践发现分区较多的情况下,该操作比较耗时
metrics = producer.metrics()
print(metrics)
producer.flush()
实践中遇到错误: kafka.errors.NoBrokersAvailable: NoBrokersAvailable,解决方案如下:
进入到配置目录(config),编辑server.properties文件,查找并设置listener,配置监听端口,格式:listeners = listener_name://host_name:port,供kafka客户端连接用的ip和端口,例中配置如下:
listeners=PLAINTEXT://127.0.0.1:9092
参考API:https://kafka-python.readthedocs.io/en/master/apidoc/KafkaConsumer.html
消费者代码不是线程安全的,最好不要用多线程
class kafka.KafkaConsumer(*topics, **configs)
*topics (str) – 可选,设置需要订阅的topic,如果未设置,需要在消费记录前调用subscribe或者assign。
subscribe(topics=(), pattern=None, listener=None)
订阅需要的主题
metrics(raw=False)
获取消费者性能指标。
#-*- encoding:utf-8 -*-
from kafka import KafkaConsumer
from kafka import TopicPartition
import json
consumer = KafkaConsumer(
'MY_TOPIC1',
bootstrap_servers=['127.0.0.1:9092'],
auto_offset_reset='latest', # 消费 kafka 中最近的数据,如果设置为 earliest 则消费最早的未被消费的数据
enable_auto_commit=True, # 自动提交消费者的 offset
auto_commit_interval_ms=3000, # 自动提交消费者 offset 的时间间隔
group_id='MY_GROUP1',
consumer_timeout_ms= 10000, # 如果 10 秒内 kafka 中没有可供消费的数据,自动退出
client_id='consumer-python3'
)
for msg in consumer:
print (msg)
print('topic: ', msg.topic)
print('partition: ', msg.partition)
print('key: ', msg.key, 'value: ', msg.value)
print('offset:', msg.offset)
print('headers:', msg.headers)
# Get consumer metrics
metrics = consumer.metrics()
print(metrics)
# 通过assign、subscribe两者之一为消费者设置消费的主题
consumer = KafkaConsumer(
bootstrap_servers=['127.0.0.1:9092'],
auto_offset_reset='latest',
enable_auto_commit=True, # 自动提交消费数据的 offset
consumer_timeout_ms= 10000, # 如果 10 秒内 kafka 中没有可供消费的数据,自动退出
value_deserializer=lambda m: json.loads(m.decode('ascii')), #消费json 格式的消息
client_id='consumer-python3'
)
# consumer.assign([TopicPartition('MY_TOPIC1', 0)])
# msg = next(consumer)
# print(msg)
consumer.subscribe('MY_TOPIC1')
for msg in consumer:
print (msg)
class kafka.client.KafkaClient(**configs)
brokers()
获取所有broker元数据
available_partitions_for_topic(topic)
返回主题的所有分区
#-*- encoding:utf-8 -*-
from kafka.client import KafkaClient
client = KafkaClient(bootstrap_servers=['127.0.0.1:9092'], request_timeout_ms=3000)
# 获取所有broker
brokers = client.cluster.brokers()
for broker in brokers:
print('broker: ', broker) # broker: BrokerMetadata(nodeId=0, host='127.0.0.1', port=9092, rack=None)
print('broker nodeId: ', broker.nodeId) # broker nodeId: 0
# 获取主题的所有分区
topic = 'MY_TOPIC1'
partitions = client.cluster.available_partitions_for_topic(topic)
print(partitions) # {0}
partition_dict = {}
partition_dict[topic] = [partition for partition in partitions]
print(partition_dict) # {'MY_TOPIC1': [0]}
class kafka.client.KafkaAdminClient(**configs)
list_topics()
获取所有的 topic
create_partitions(topic_partitions,timeout_ms = None,validate_only = False )
为现有主题创建其他分区。返回值:合适版本的CreatePartitionsResponse类。
create_topics(new_topics,timeout_ms = None,validate_only = False )
在集群中创建新主题。返回值:合适版本的CreateTopicResponse类。
delete_topics(主题,timeout_ms =无)
从集群中删除主题。返回值:合适版本的DeleteTopicsResponse类。
describe_consumer_groups(group_ids,group_coordinator_id = None,include_authorized_operations = False)
描述一组消费者group。返回值:组说明列表。目前,组描述是DescribeGroupsResponse的原始结果。
list_consumer_group_offsets(group_id,group_coordinator_id = None,partitions = None)
获取单个消费者组的消费者offset。注意:这不会验证group_id或分区在集群中是否实际存在。一旦遇到任何错误,就会立即报错。 返回字典:具有TopicPartition键和OffsetAndMetada值的字典。省略未指定且group_id没有记录偏移的分区。偏移值-1表示group_id对于该TopicPartition没有偏移。一个-1只能发生于显式指定的分区。
list_consumer_groups(broker_ids = None)
列出集群已知的所有消费者组。这将返回消费者组元组的列表。元组由使用者组名称和使用者组协议类型组成。仅返回将偏移量存储在Kafka中的消费者组。对于使用Kafka <0.9 API创建的群组,协议类型将为空字符串,因为尽管它们将偏移量存储在Kafka中,但它们并不使用Kafka进行群组协调。对于使用Kafka> = 0.9创建的群组,协议类型通常为“消费者”。
from kafka.admin import KafkaAdminClient, NewTopic
client = KafkaAdminClient(bootstrap_servers="localhost:9092")
topic_list = []
# 创建自定义分区的topic 可以使用以下方法创建名称为test,12个分区3份副本的topic
topic_list.append(NewTopic(name="test", num_partitions=12, replication_factor=3))
client.create_topics(new_topics=topic_list, validate_only=False)
# 获取所有的 topic
client.list_topics()
# 删除 topic
client.delete_topics(['test', 'ssl_test']) # 传入要删除的 topic 列表
# list_consumer_groups()的返回值是一个元组(消费者组的名称,消费组协议类型)组成的列表。
client.list_consumer_groups()
# [('xray', 'consumer'), ('awvs', 'consumer')]
# 返回值是一个字典,字典的key是TopicPartition,值是OffsetAndMetada
client.list_consumer_group_offsets('awvs')
# {TopicPartition(topic='scan, partition=0): OffsetAndMetadata(offset=17, metadata='')