Kafka数据消费:快速上手

Kafka数据消费:快速上手

Kafka逻辑模型

整体流程

由一些Producer进程向Kafka的服务器(称为Broker)发送消息,这些消息以topic区分,消息本身由key,value对和其他的属性字段组成;Broker接收来自Producer的消息(按照topic区分)之后,按照分区策略分到不同的partition,每一个partition内的消息是有序的队列,并按照副本策略复制指定次数;当Consumer进程连接到Broker后,订阅(subscribe)特定的topic,就可以开始消费数据,即从topic的多个partition中获得(poll)数据。

存储逻辑模型

Kafka在理论上提供一个稳定且长久保存的消息队列,但实际上因为磁盘空间有限或者策略设置,会将旧的数据清除掉;当Broker收到Producer发送的一条消息后,消息被送到其中的一个分区中,并将该分区的结束位置偏移量(end offset)向后增加1;当根据清理策略将旧的一条消息清除后,开始位置偏移量(begin offset)向后增加1。

Kafka数据消费

消费者组

Kafka在理论上提供并发和可靠的消息消费,对于一个消费者组(Consumer group)来说,topic下的多个partition会被分配(assign)到Consumer group中的相同或不同Consumer上(取决于partition数量和Consumer数量,如果二者数量相等,则每个Consumer将从每个被固定分配的partition中读取数据)。

将Consumer group看作一个整体,每次新的Consumer group初始化的时候,依照策略将当前的读取位置偏移量(position)移动到begin offset或end offset上;在每次从某个partition中poll数据后,应当将该group在该partition中的position向后移动此次poll的数据量(这个过程称为commit,如果设置了auto commit,则该过程和poll过程捆绑进行),从而记录下该分区的数据已经消费到何处了,从而避免数据的重复消费。

数据获取

Kafka提供的数据消费接口是poll(获取),poll的执行单位是Consumer group,当执行poll的时候,实际的执行流程伪代码(仅用于理解具体过程,如果不需要,可以直接跳到下一节)为:

function poll(Consumer_group g)
{
polled_data = Dict()
foreach Consumer c in g: // 对于组里的每个Consumer c
{
	if not assigned(consumer_id=c){ // 如果c没有被分配topic中的分区
	assign_partition(consumer_id=c) // 就分配若干分区
	}
	Partition_list pars = get_assigned_partitions(consumer_id=c) // 如果已经分配,则获取分配的分区
	foreach Partirion par in pars: // 对于分配的每个分区par
	{
		if not par.is_paused(){ // 如果没有被暂停消费
			pos = par.get_position(group_id=g) // 获得c在该分区当前的偏移量position
			data = fetch_data_from_partition(partition=par, start_at=pos) // 从position向后取若干数据
			polled_data[par] = data // 加入缓冲区
			if auto_commit{ // 如果设置了自动提交
				par.set_position(group_id=g, by=lambda x: x+=data.length) // 则将position更新
			}
		}
	}
}
return polled_data
}

使用kafka-python库消费数据:实例

import kafka
# instantiate a consumer 
consumer_instance = kafka.KafkaConsumer(
    group_id='test-group',
    bootstrap_servers='192.100.10.1:9092',
    security_protocol='SASL_PLAINTEXT',
    sasl_mechanism='PLAIN',
    sasl_plain_username='user',
    sasl_plain_password='password',
    auto_offset_reset='earliest',
    enable_auto_commit=False,
)
# test connectivity with broker
print(consumer_instance.bootstrap_connected())
# subscribe to topics 
consumer_instance.subscribe(topics=['test_topic'])
print(consumer_instance.subscription())
# get partition info of the topic
print(consumer_instance.partitions_for_topic('test_topic'))
# the first poll returns no data but get the assigned partitions of each consumer
msg = consumer_instance.poll(timeout_ms=2000)  # need timeout in case no enough time for assignment
pars = consumer_instance.assignment()
print(pars)
# get the position of each partition for current consumer group
for par in pars:
	print(consumer_instance.position(par))
# poll data in a loop
while True:
	msg = consumer_instance.poll(timeout_ms=2000, max_records=10000)
	consumer_instance.commit()
	print(msg)

输出结果如下:

True
{'test_topic'}
{0, 1, 2}
{TopicPartition(topic='test_topic', partition=2), TopicPartition(topic='test_topic', partition=0), TopicPartition(topic='test_topic', partition=1)}
9093087
9239726
9245694
{}
{TopicPartition(topic='test_topic', partition=0): [ConsumerRecord(topic='test_topic', partition=0, offset=9239726, timestamp=1644807569036, timestamp_type=0, key=None, value=b'...(此处省略消息内容)', headers=[], checksum=None, serialized_key_size=-1, serialized_value_size=1277, serialized_header_size=-1),ConsumerRecord(topic='test_topic', partition=0, offset=9239727, timestamp=1644807569036, timestamp_type=0, key=None, value=b'...(此处省略消息内容)', headers=[], checksum=None, serialized_key_size=-1, serialized_value_size=1277, serialized_header_size=-1),ConsumerRecord(topic='test_topic', partition=0, offset=9239728, timestamp=1644807569036, timestamp_type=0, key=None, value=b'...(此处省略消息内容)', headers=[], checksum=None, serialized_key_size=-1, serialized_value_size=1277, serialized_header_size=-1),...(此处省略后续的ConsumerRecord)]} 
...(此处省略后续的数据)

使用kafka-python库消费数据:KafkaConsumer类的更多方法

kafka-python库的文档 https://kafka-python.readthedocs.io/
KafkaConsumer类的文档 https://kafka-python.readthedocs.io/en/master/apidoc/KafkaConsumer.html

以下内容基于kafka-python 2.0.2版本

  • bootstrap_connected:是否已经连接到指定Broker
  • close:断开连接
  • topics:获取Broker上所有有权查看的topic
  • subscribe(topics=(), pattern=None, listener=None):订阅topic,可以订阅多个
  • unsubscribe:取消所有topic订阅
  • subscription:查看订阅的所有topic
  • partitions_for_topic:获取某个topic的partition列表
  • poll(timeout_ms=0, max_records=None, update_offsets=True):按照指定参数获取数据,首次运行将自动assign分区
  • assign:手动assign分区
  • assignment:查看assign的分区
  • beginning_offsets、end_offsets:查看分区的开始位置偏移量(begin offset)和结束位置偏移量(end offset)
  • position:查看分区的当前的读取位置偏移量(position)
  • seek:将分区的position移动到指定的偏移量位置
  • seek_to_beginning,seek_to_end:将position移动到分区的begin offset/end offset
  • commit、commit_async:同步/异步地将存储在本地的分区position更新到服务器端
  • committed:查看分区position是否更新到服务器端
  • pause、resume:暂停/重启某partition的消费,当某个分区pause后,获取数据为空,直到resume
  • paused:查看分区是否pause

猜你喜欢

转载自blog.csdn.net/O_1CxH/article/details/123008476
今日推荐