• Sparkstreaming and Kafka


    简介

    Kafka 0.10的Spark Streaming集成设计与0.8 Direct Stream方法类似。 它提供了简单的并行性,Kafka分区和Spark分区之间的1:1对应关系,以及对偏移量和元数据的访问。 但是,由于较新的集成使用新的Kafka消费者API而不是简单的API,所以在使用上存在显着差异。 这个版本的集成被标记为实验,所以API可能会有变化。

    LINK(依赖)

    对于使用SBT / Maven项目定义的Scala / Java应用程序,请将您的流应用程序与以下工件链接起来。

    1 groupId = org.apache.spark
    2 artifactId = spark-streaming-kafka-0-10_2.11
    3 version = 2.2.0

    不要在org.apache.kafka构件上手动添加依赖项(例如kafka-clients)。 spark-streaming-kafka-0-10已经具有适当的传递依赖性,不同的版本可能在诊断的方式上不兼容。

    Creating a Direct Stream

    请注意,导入的名称空间包含版本org.apache.spark.streaming.kafka010

     1 import org.apache.kafka.clients.consumer.ConsumerRecord
     2 import org.apache.kafka.common.serialization.StringDeserializer
     3 import org.apache.spark.streaming.kafka010._
     4 import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
     5 import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
     6 
     7 val kafkaParams = Map[String, Object](
     8   "bootstrap.servers" -> "localhost:9092,anotherhost:9092",
     9   "key.deserializer" -> classOf[StringDeserializer],
    10   "value.deserializer" -> classOf[StringDeserializer],
    11   "group.id" -> "use_a_separate_group_id_for_each_stream",
    12   "auto.offset.reset" -> "latest",
    13   "enable.auto.commit" -> (false: java.lang.Boolean)
    14 )
    15 
    16 val topics = Array("topicA", "topicB")
    17 val stream = KafkaUtils.createDirectStream[String, String](
    18   streamingContext,
    19   PreferConsistent,
    20   Subscribe[String, String](topics, kafkaParams)
    21 )
    22 
    23 stream.map(record => (record.key, record.value))

    流中的每个项目都是一个ConsumerRecord(消费者记录)

    有关可能的kafkaParams,请参阅Kafka使用者配置文档。 如果您的Spark批处理持续时间大于默认Kafka心跳会话超时(30秒),请适当增加heartbeat.interval.ms和session.timeout.ms。 对于大于5分钟的批次,这将需要更改代理上的group.max.session.timeout.ms。 请注意,该示例将enable.auto.commit设置为false,有关讨论,请参阅下面的“存储偏移量”。

    LocationStrategies(位置策略)

    新的Kafka消费者API将预取消息到缓冲区中。因此,性能方面的原因是Spark集成将缓存的消费者保留在执行者上(而不是为每个批次重新创建它们),并且倾向于在具有相应使用者的主机位置上调度分区。

    在大多数情况下,您应该使用LocationStrategies.PreferConsistent,如上所示。这会将分区平均分配给可用的执行者。如果您的执行者(executors )与您的Kafka经纪人(brokers)位于同一个主机上,请使用PreferBrokers,它将优先为该分区的Kafka领导安排分区。最后,如果分区间负载存在明显偏移,请使用PreferFixed。这允许您指定分区到主机的明确映射(任何未指定的分区将使用一致的位置)。

    消费者缓存的默认最大大小为64.如果您希望处理多于(执行者数量为64 *)的Kafka分区,则可以通过spark.streaming.kafka.consumer.cache.maxCapacity更改此设置。

    如果您想禁用Kafka使用者的缓存,则可以将spark.streaming.kafka.consumer.cache.enabled设置为false。可能需要禁用缓存来解决SPARK-19185中描述的问题。 SPARK-19185解决后,该属性可能会在更高版本的Spark中删除。

    缓存由topicpartition和group.id键入,因此每次调用createDirectStream时都要使用一个单独的group.id。

    ConsumerStrategies(消费者策略)

    新的Kafka消费者API有许多不同的方式来指定主题,其中一些需要大量的后对象实例化设置。 ConsumerStrategies提供了一个抽象,允许Spark从检查点重新启动后即可获得正确配置的使用者。

    ConsumerStrategies.Subscribe,如上所示,允许您订阅固定的主题集合。 SubscribePattern允许您使用正则表达式来指定感兴趣的主题。 请注意,与0.8集成不同,使用Subscribe或SubscribePattern应该在正在运行的流中添加分区。 最后,Assign允许你指定一个固定的分区集合。 所有这三种策略都有重载的构造函数,允许您指定特定分区的起始偏移量。

    如果您具有以上选项不能满足的特定消费者设置需求,则ConsumerStrategy是可以扩展的公共类。

    Creating an RDD

    如果您有一个更适合批处理的用例,则可以为已定义的偏移范围创建一个RDD。

    1 // Import dependencies and create kafka params as in Create Direct Stream above
    2 
    3 val offsetRanges = Array(
    4   // topic, partition, inclusive starting offset, exclusive ending offset
    5   OffsetRange("test", 0, 0, 100),
    6   OffsetRange("test", 1, 0, 100)
    7 )
    8 
    9 val rdd = KafkaUtils.createRDD[String, String](sparkContext, kafkaParams, offsetRanges, PreferConsistent)

    请注意,您不能使用PreferBrokers,因为如果没有流,则不存在驱动程序方面的消费者为您自动查找代理元数据。 如有必要,请使用PreferFixed与您自己的元数据查找。

    Obtaining Offsets

    1 stream.foreachRDD { rdd =>
    2   val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
    3   rdd.foreachPartition { iter =>
    4     val o: OffsetRange = offsetRanges(TaskContext.get.partitionId)
    5     println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
    6   }
    7 }

    请注意,HasOffsetRanges的类型转换只会在createDirectStream的结果中调用的第一个方法中完成,而不是稍后向下的一系列方法。 请注意,RDD分区与Kafka分区之间的一对一映射在任何混洗或重新分区的方法(例如, reduceByKey()或window()。

    Storing Offsets

    Kafka交付语义在失败的情况下取决于如何以及何时存储偏移量。 spark输出操作至少一次。 所以,如果你想要相当于一次的语义,你必须在幂等输出之后存储偏移量,或者在输出中存储原子事务的偏移量。 通过这种集成,您可以选择3个选项,以提高可靠性(和代码复杂度),以便如何存储偏移量。

    Checkpoints

    如果启用了Spark检查点,偏移量将被存储在检查点中。 这很容易启用,但也有缺点。 你的输出操作必须是幂等的,因为你将得到重复的输出; 转换不是一种选择。 此外,如果应用程序代码已更改,则无法从检查点恢复。 对于计划升级,可以通过在旧代码的同时运行新代码来缓解这种情况(因为无论如何输出需要是幂等的,它们不应该发生冲突)。 但是对于需要更改代码的意外故障,除非您有另外的方法来识别已知的良好起始偏移量,否则将会丢失数据。

    Kafka itself

    Kafka有一个偏移提交API,用于在特殊的Kafka主题中存储偏移量。 默认情况下,新的使用者将定期自动提交偏移量。 这几乎肯定不是你想要的,因为由消费者成功轮询的消息可能还没有导致Spark输出操作,导致未定义的语义。 这就是为什么上面的流示例将“enable.auto.commit”设置为false的原因。 但是,在知道输出已存储之后,可以使用commitAsync API将偏移量提交给Kafka。 与检查点相比,好处在于,无论应用程序代码如何变化,Kafka都是耐用的商店。 然而,Kafka不是转换型的,所以你的输出仍然是幂等的。

    1 stream.foreachRDD { rdd =>
    2   val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
    3 
    4   // some time later, after outputs have completed
    5   stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
    6 }

    和HasOffsetRanges一样,如果调用createDirectStream的结果,而不是在转换之后,转换为CanCommitOffsets将会成功。 commitAsync调用是线程安全的,但是如果你想要有意义的语义,则必须在输出之后进行。

    Your own data store

    对于支持事务的数据存储,即使在出现故障的情况下,也可以在同一个事务中保存偏移量,以保持两者同步。 如果仔细检测重复或跳过的偏移量范围,回滚事务将防止重复或丢失的消息影响结果。 这给出了相当于一次的语义。 甚至可以使用这种策略,即使对于通常难以产生幂等性的聚合产生的输出也是如此。

     1 // The details depend on your data store, but the general idea looks like this
     2 
     3 // begin from the the offsets committed to the database
     4 val fromOffsets = selectOffsetsFromYourDatabase.map { resultSet =>
     5   new TopicPartition(resultSet.string("topic"), resultSet.int("partition")) -> resultSet.long("offset")
     6 }.toMap
     7 
     8 val stream = KafkaUtils.createDirectStream[String, String](
     9   streamingContext,
    10   PreferConsistent,
    11   Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
    12 )
    13 
    14 stream.foreachRDD { rdd =>
    15   val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
    16 
    17   val results = yourCalculation(rdd)
    18 
    19   // begin your transaction
    20 
    21   // update results
    22   // update offsets where the end of existing offsets matches the beginning of this batch of offsets
    23   // assert that offsets were updated correctly
    24 
    25   // end your transaction
    26 }

    SSL / TLS

    新的Kafka使用者支持SSL。 要启用它,请在传递给createDirectStream / createRDD之前适当地设置kafkaParams。 请注意,这仅适用于Spark和Kafkabroker之间的沟通; 您仍负责单独确保Spark节点间通信。

    1 val kafkaParams = Map[String, Object](
    2   // the usual params, make sure to change the port in bootstrap.servers if 9092 is not TLS
    3   "security.protocol" -> "SSL",
    4   "ssl.truststore.location" -> "/some-directory/kafka.client.truststore.jks",
    5   "ssl.truststore.password" -> "test1234",
    6   "ssl.keystore.location" -> "/some-directory/kafka.client.keystore.jks",
    7   "ssl.keystore.password" -> "test1234",
    8   "ssl.key.password" -> "test1234"
    9 )

    Deploying

    与任何Spark应用程序一样,spark-submit用于启动您的应用程序。

    对于Scala和Java应用程序,如果您使用SBT或Maven进行项目管理,请将spark-streaming-kafka-0-10_2.11及其依赖项打包到应用程序JAR中。 确保spark-core_2.11和spark-streaming_2.11被标记为提供的依赖关系,因为这些已经存在于Spark安装中。 然后使用spark-submit启动您的应用程序(请参阅主编程指南中的部署)。

     
  • 相关阅读:
    网页中这 10 种字体的运用方式,不会让人觉得 Low
    如何估算文章阅读时长?
    如何养出一个三十几亿身家的儿子
    2018免费的隐私保护工具
    写一份好的产品说明书
    安装 Ubuntu 19.10 选用 zfs 文件系统
    ESXI常用命令
    Harbor ($docker login) Error saving credentials
    Vue中使用matomo进行访问流量统计的实现
    eslint Cannot read property 'range' of null错误
  • 原文地址:https://www.cnblogs.com/hmy-blog/p/7799204.html
Copyright © 2020-2023  润新知