Scala 使用Kafka在长时间运行的Spark作业之间进行通信
我是ApacheSpark新手,需要在我的Spark集群上同时运行几个长期运行的进程(作业)。通常,这些单独的流程(每个流程都是自己的工作)需要相互通信。我暂时考虑使用卡夫卡作为这些过程之间的中间人。因此,高级别的工作对工作沟通如下所示:Scala 使用Kafka在长时间运行的Spark作业之间进行通信,scala,apache-spark,apache-kafka,spark-streaming,long-running-processes,Scala,Apache Spark,Apache Kafka,Spark Streaming,Long Running Processes,我是ApacheSpark新手,需要在我的Spark集群上同时运行几个长期运行的进程(作业)。通常,这些单独的流程(每个流程都是自己的工作)需要相互通信。我暂时考虑使用卡夫卡作为这些过程之间的中间人。因此,高级别的工作对工作沟通如下所示: 作业#1做一些工作,并将消息发布到卡夫卡主题 作业#2被设置为同一卡夫卡主题的流式接收器(使用StreamingContext),消息发布到该主题后,作业#2就会使用它 作业#2现在可以根据它所使用的消息执行一些工作 据我所知,流式上下文正在阻止Spark驱动
StreamingContext
),消息发布到该主题后,作业#2就会使用它def createKafkaStream(ssc: StreamingContext,
kafkaTopics: String, brokers: String): DStream[(String,
String)] = {
// some configs here
KafkaUtils.createDirectStream[String, String, StringDecoder,
StringDecoder](ssc, props, topicsSet)
}
def consumerHandler(): StreamingContext = {
val ssc = new StreamingContext(sc, Seconds(10))
createKafkaStream(ssc, "someTopic", "my-kafka-ip:9092").foreachRDD(rdd => {
rdd.collect().foreach { msg =>
// Now do some work as soon as we receive a messsage from the topic
}
})
ssc
}
StreamingContext.getActive.foreach {
_.stop(stopSparkContext = false)
}
val ssc = StreamingContext.getActiveOrCreate(consumerHandler)
ssc.start()
ssc.awaitTermination()
…现在有两种含义:
StreamingContext
(单数)不是阻塞侦听器。它的任务是为流媒体作业创建执行图
当您开始从卡夫卡读取时,您指定希望每10秒获取一次新记录。从现在起会发生什么取决于您对卡夫卡使用的卡夫卡抽象,或者是通过KafkaUtils.createStream
的接收方方法,或者是通过KafkaUtils.createDirectStream
的无接收方方法
通常,在这两种方法中,数据都是从卡夫卡中消耗出来的,然后发送给每个Spark worker进行并行处理
然后我只是想知道是否有更具可扩展性或性能的
如何做到这一点
这种方法具有高度可扩展性。使用无接收器方法时,每个Kafka分区映射到给定RDD中的Spark分区。您可以通过增加Kafka中的分区数量或通过重新分区Spark中的数据(使用
DStream.repartition
)来提高并行性。我建议测试此设置,以确定它是否符合您的性能要求。谢谢@Yuval(+1),如果您不介意的话,请回答以下几个问题!(1) 您能否首先确认,为了针对Spark上的卡夫卡主题设置“竞争消费者”,我需要每簇1个消费者?对于接收器和无接收器配置,这是真的吗?(2) 什么时候使用接收器与无接收器方法的一般指导原则是什么?(3)当您说“当您开始从卡夫卡读取时,您指定您希望每10秒获取一次新记录…”时,这是在哪里配置的?它可以设置为10秒以外的值吗?最后,(4)使用接收方方法时,Kafka分区映射到什么?再次非常感谢@smeeb 1)你所说的“竞争消费者”是什么意思?2) 我通常建议使用直接流式方法,它是在Spark 1.3.0中引入的,具有许多优点。我建议好好读一读。3) 在这里配置:val ssc=new StreamingContext(sc,秒(10))
。4) 在基于接收器的方法中,没有分区映射。如果您想同时从Kafka读入,您必须连接多个使用者,这意味着您必须执行多个KafkaUtils.createStream
调用并合并它们。再次感谢@Yuval(+1)!所谓“竞争消费者”,我指的是多个消费者线程都在收听和消费来自同一卡夫卡主题的消息。因此,我上面的第一个问题是想澄清我对Spark上的流媒体消费者的理解,即:每个并发消费者必须在自己的Spark集群上运行(对还是错?)。或者我可以从consumerHandler
内部多次调用KafkaUtils.createDStream
,每次调用都会为我设置一个不同的“竞争”消费者线程(在同一集群上)。如果使用直接流方法,则两者都不需要。Spark将在工人之间划分卡夫卡偏移量,工人可以同时读取卡夫卡。直接流方法中的基数是kafka分区和spark RDD分区之间的1:1。阅读主题的并发性基于spark Worker的数量,spark Worker可以使用rdd从Kafka.BTW读取偏移量。foreachRDD中的collect
将导致它们将整个数据集发送回驱动程序。你肯定不想这样。谢谢@Yuval(+1),有什么更好/更有效的方法可以访问正在使用的单个邮件?这不是我的初衷,我只是刚刚接触API,所以请随时更新我的代码!您可以使用rdd.foreach
。