pySpark Kafka Direct Streaming更新Zookeeper / Kafka Offset

时间:2017-05-22 10:00:41

标签: python pyspark apache-kafka spark-streaming apache-zookeeper

目前我正在与Kafka / Zookeeper和pySpark(1.6.0)合作。 我已经成功创建了一个使用KafkaUtils.createDirectStream()

的kafka使用者

所有流媒体都没有问题,但我认识到,在我消费了一些消息之后,我的Kafka主题没有更新到当前的偏移量。

由于我们需要更新主题以在此处进行监控,因此这有点奇怪。

在Spark的文档中我发现了这个评论:

   offsetRanges = []

     def storeOffsetRanges(rdd):
         global offsetRanges
         offsetRanges = rdd.offsetRanges()
         return rdd

     def printOffsetRanges(rdd):
         for o in offsetRanges:
             print "%s %s %s %s" % (o.topic, o.partition, o.fromOffset, o.untilOffset)

     directKafkaStream\
         .transform(storeOffsetRanges)\
         .foreachRDD(printOffsetRanges)
  

如果您希望基于Zookeeper的Kafka监控工具显示流应用程序的进度,您可以使用它来自行更新Zookeeper。

以下是文档: http://spark.apache.org/docs/1.6.0/streaming-kafka-integration.html#approach-2-direct-approach-no-receivers

我在Scala中找到了一个解决方案,但我找不到python的等价物。 这是Scala示例:http://geeks.aretotally.in/spark-streaming-kafka-direct-api-store-offsets-in-zk/

问题

但问题是,我怎么能从那时起更新zookeeper?

2 个答案:

答案 0 :(得分:1)

我遇到了类似的问题。 你是对的,通过使用directStream,意味着直接使用kafka低级API,它没有更新读者偏移量。 scala / java有几个例子,但不适用于python。 但是你自己很容易做到,你需要做的是:

  • 从开头的偏移量中读取
  • 在末尾保存偏移量

例如,我通过执行以下操作来保存redis中每个分区的偏移量:

stream.foreachRDD(lambda rdd: save_offset(rdd))
def save_offset(rdd):
  ranges = rdd.offsetRanges()
  for rng in ranges:
     rng.untilOffset # save offset somewhere

然后在开始时,您可以使用:

fromoffset = {}
topic_partition = TopicAndPartition(topic, partition)
fromoffset[topic_partition]= int(value) #the value of int read from where you store previously.

对于一些使用zk跟踪偏移量的工具,最好在zookeeper中保存偏移量。 这一页: https://community.hortonworks.com/articles/81357/manually-resetting-offset-for-a-kafka-topic.html 描述如何设置偏移量,基本上,zk节点是: / consumers / [consumer_name] / offsets / [topic name] / [partition id] 因为我们正在使用directStream,所以你必须组成一个消费者名称。

答案 1 :(得分:1)

我编写了一些函数来保存和读取使用python kazoo库的Kafka偏移量。

第一个获取Kazoo Client单例的函数:

ZOOKEEPER_SERVERS = "127.0.0.1:2181"

def get_zookeeper_instance():
    from kazoo.client import KazooClient

    if 'KazooSingletonInstance' not in globals():
        globals()['KazooSingletonInstance'] = KazooClient(ZOOKEEPER_SERVERS)
        globals()['KazooSingletonInstance'].start()
    return globals()['KazooSingletonInstance']

然后用于读取和写入偏移:

def read_offsets(zk, topics):
    from pyspark.streaming.kafka import TopicAndPartition

    from_offsets = {}
    for topic in topics:
        for partition in zk.get_children(f'/consumers/{topic}'):
            topic_partion = TopicAndPartition(topic, int(partition))
            offset = int(zk.get(f'/consumers/{topic}/{partition}')[0])
            from_offsets[topic_partion] = offset
    return from_offsets

def save_offsets(rdd):
    zk = get_zookeeper_instance()
    for offset in rdd.offsetRanges():
        path = f"/consumers/{offset.topic}/{offset.partition}"
        zk.ensure_path(path)
        zk.set(path, str(offset.untilOffset).encode())

然后在开始流式传输之前,您可以从zookeeper读取偏移并将它们传递给createDirectStream for fromOffsets argument。:

from pyspark import SparkContext
from pyspark.streaming import StreamingContext
from pyspark.streaming.kafka import KafkaUtils


def main(brokers="127.0.0.1:9092", topics=['test1', 'test2']):
    sc = SparkContext(appName="PythonStreamingSaveOffsets")
    ssc = StreamingContext(sc, 2)

    zk = get_zookeeper_instance()
    from_offsets = read_offsets(zk, topics)

    directKafkaStream = KafkaUtils.createDirectStream(
        ssc, topics, {"metadata.broker.list": brokers},
        fromOffsets=from_offsets)

    directKafkaStream.foreachRDD(save_offsets)


if __name__ == "__main__":
    main()