【发布时间】:2022-10-01 02:05:02
【问题描述】:
我正在尝试将 Python 代码从 aiokafka 切换到 confluent_kafka 并且在读取历史数据时遇到问题。
系统对于给定的主题只有一个生产者,以及几个独立的消费者(每个都有一个单独的组 ID)。 当每个消费者启动时,它想读取主题子集的最新历史消息(称为历史主题),然后读取所有新消息。 历史数据的确切起点并不重要,因为重点是获取很少写的主题的信息。 需要历史数据的主题永远只有一个分区。
它得到了让我适应的历史数据。
我宁愿在搜索之前不必阅读任何消息,因为该消息可能比我想开始的要新。但似乎至少要在 Kafka 分配主题分区之前调用 Consumer.poll。
推荐的顺序是什么?
我尝试了两种基本方法:
- 使用自动主题分区分配和
on_assign回调参数Consumer.subscribe读取当前偏移量并调用seek。 - 手动分配分区并使用这些分区读取当前偏移量并调用 seek。
在这两种情况下:
-
Consumer.seek通常或总是以“本地:错误状态”失败。 -
Consumer.positions总是返回 -1001,这可能是一个线索。 为了解决这个问题,我打电话给Consumer.get_watermark_offsets。
下面是一个使用 on_assign 的简单示例:
from confluent_kafka import Consumer
from confluent_kafka.admin import AdminClient, NewTopic
from confluent_kafka.error import KafkaError
import base64
import os
max_history = 3
broker_addr = \"broker:29092\"
topic_names = [\"test.message\"]
def seek_back(
consumer,
partitions,
):
print(f\"seek_back({partitions})\")
# Show that consumer.position returns nothing useful
position_partitions = consumer.position(partitions)
print(f\"{position_partitions=}\")
for partition in partitions:
_, offset = consumer.get_watermark_offsets(partition)
print(f\"{partition.topic} has offset {offset}\")
if offset <= 0:
continue
partition.offset = max(0, offset - max_history)
try:
consumer.seek(partition)
except Exception as e:
print(f\"{partition.topic} seek to {partition.offset} failed: {e!r}\")
else:
print(f\"{partition.topic} seek to {partition.offset} succeeded\")
def run(topic_names):
random_str = base64.urlsafe_b64encode(os.urandom(12)).decode().replace(\"=\", \"_\")
consumer = Consumer(
{
\"group.id\": random_str,
\"bootstrap.servers\": broker_addr,
\"allow.auto.create.topics\": False,
}
)
new_topic_list = [
NewTopic(topic_name, num_partitions=1, replication_factor=1)
for topic_name in topic_names
]
broker_client = AdminClient({\"bootstrap.servers\": broker_addr})
create_result = broker_client.create_topics(new_topic_list)
for topic_name, future in create_result.items():
exception = future.exception()
if exception is None:
continue
elif (
isinstance(exception.args[0], KafkaError)
and exception.args[0].code() == KafkaError.TOPIC_ALREADY_EXISTS
):
pass
else:
print(f\"Failed to create topic {topic_name}: {exception!r}\")
raise exception
consumer.subscribe(topic_names, on_assign=seek_back)
while True:
message = consumer.poll(timeout=0.1)
if message is not None:
error = message.error()
if error is not None:
raise error
print(f\"read {message=}\")
return
run(topic_names)
在为该主题编写一些消息(使用其他代码)之后运行它会给我:
seek_back([TopicPartition{topic=test.topic,partition=0,offset=-1001,error=None}])
position_partitions=[TopicPartition{topic=test.topic,partition=0,offset=-1001,error=None}]
test.topic has offset 10
seek_partitions=[TopicPartition{topic=test.topic,partition=0,offset=7,error=None}]
test.topic seek to 0 failed: KafkaException(KafkaError{code=_STATE,val=-172,str=\"Failed to seek to offset 7: Local: Erroneous state\"})
我正在使用:confluent_kafka 1.8.2 并使用 Docker 映像 confluentinc/cp-enterprise-kafka:6.2.4 运行代理 (以及相同版本的 zookeper 和模式注册表,因为我的正常代码使用 Avro 模式)。
-
在调用 subscribe 后立即分配分区似乎有点帮助:然后 seek 成功,但代码仍然没有读取历史数据(poll 一直返回 None),并且即使调用 consumer.poll,consumer.position 仍然返回 unknown
-
-1001是OFFSET_INVALIDgithub.com/edenhill/librdkafka/blob/master/src/rdkafka.h#L3498
标签: python apache-kafka confluent-kafka-python