SparkStreamingがKafkaを消費する2つの方法

pomの依存関係をインポートする必要があります

<dependency>
     <groupId>org.apache.spark</groupId>
     <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
     <version>2.0.2</version>
</dependency>

1つは、レシーバーの読み取り方法です。

このように、Receiverはデータを取得するために使用されます。Receiverは、Kafkaの高レベルのコンシューマーAPIを使用してデータ消費を実現します。

受信者がKafkaから取得したデータは、Spark Executorのメモリに保存され、SparkStreamingによって開始されたジョブがデータを処理します。

ただし、デフォルトの構成では、Kafkaの高度なAPIが消費中にオフセットを維持しないため、このメソッドは、基になるレイヤーの障害のためにデータを失う可能性があります。

信頼性の高いメカニズムとデータ損失をゼロにするには、Spark Streamingの先行書き込みログ(WAL)メカニズムを有効にします。このメカニズムは、受信したKafkaデータを分散ファイルシステム(HDFSなど)の先行書き込みログに同期的に書き込みます。したがって、スパークタスクが失敗した場合でも、先行書き込みログのデータを回復に使用できます。

注意点

1.このように、Kafkaで消費されるトピックのパーティションは、SparkのRDDのパーティションとは何の関係もありません。したがって、KafkaUtils.createStream()では、パーティションの数を増やすと、Receiverがkafka_partitionを読み取るスレッドの数だけが増えます。Spark処理データの並列処理は増加しません。

2.複数のKafka入力DStreamを作成し、さまざまなコンシューマーグループとトピックを使用して、複数のレシーバーを介してデータを並行して受信できます。

3. HDFSなどのフォールトトレラントファイルシステムで先行書き込みログメカニズムが有効になっている場合、受信したデータは先行書き込みログにコピーされます。したがって、KafkaUtils.createStream()では、永続レベルセットはStorageLevel.MEMORY_AND_DISK_SERです。

4. KafkaUtilsは、新しいバージョンのjarでレシーバーによって読み取られたAPIを削除しました

削除されましたが、実装方法を知る必要があります。

package com.spark

import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{
    
    Seconds, StreamingContext}
import org.apache.spark.{
    
    SparkContext, SparkConf}


object SparkStreamingReceiverKafka {
    
    
  def main(args: Array[String]) {
    
    
    val conf = new SparkConf()
    conf.setAppName("SparkStreamingReceiverKafka")
    conf.set("spark.streaming.kafka.maxRatePerPartition", "10")
	conf.set("spark.streaming.receiver.writeAheadLog.enable", "true")//预写日志需要改这个配置
    conf.setMaster("local[2]")

    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    val ssc = new StreamingContext(sc, Seconds(5)) // 创建streamingcontext入口

	ssc.checkpoint("hdfs://localhost:9000/log")//预写日志的hdfs地址需要通过checkpoint设置
	
    val zks = "zk1,zk2,zk3"
    val groupId = "kafka_spark_xf"
    val map : Map[String, Int] = Map("kafka_spark" -> 2) // topic名称为kafka_spark,每次使用2个线程读取数据

	//参数: 流对象 zookeeper集群 消费者id map参数,日志等级必须要有落盘操作
    val dframe = KafkaUtils.createStream(ssc, zks, groupId, map, StorageLevel.MEMORY_AND_DISK_SER_2)
    
    dframe.foreachRDD(rdd => {
    
     // 操作方式和rdd差别不大
      rdd.foreachPartition(partition =>{
    
    
        partition.foreach(println)
      })
    })
  }
}

次に、ダイレクトモードで読み取ります

より堅牢なメカニズムを確保するために、Receiverに基づかないこの新しい直接メソッドがSpark1.3で導入されました。このメソッドは、Receiverを使用してデータを受信する代わりに、定期的にKafkaにクエリを実行して、各トピックとパーティションの最新のオフセットを取得します。これにより、各バッチのオフセットの範囲が定義されます。データを処理するジョブが開始されると、Kafkaの単純なコンシューマーAPIを使用して、Kafkaの指定されたオフセット範囲のデータが取得されます。

この方法には、次の利点があります。

1.並列読み取りを簡素化します。複数のパーティションを読み取る場合は、複数の入力DStreamを作成してから、それらに対してユニオン操作を実行する必要はありません。SparkはKafkaパーティションと同じ数のRDDパーティションを作成し、Kafkaからデータを並行して読み取ります。したがって、KafkaパーティションとRDDパーティションの間には1対1のマッピング関係があります。

2.高性能:データ損失をゼロにしたい場合は、レシーバーベースの方法でWALメカニズムをオンにする必要があります。この方法は、データが実際に2回コピーされるため、実際には非効率的です。Kafka自体には、データの1つのコピーをコピーしてから1つのコピーをWALにコピーする信頼性の高いメカニズムがあります。直接方式に基づいており、Receiverに依存せず、WALメカニズムをオンにする必要もありません。データがKafkaで高可用性である限り、Kafkaのコピーを介して復元できます。

3.一度だけのトランザクションメカニズム:

レシーバーメソッドに基づいて、Kafkaの高レベルAPIを使用して、ZooKeeperで消費されたオフセットを保存します。これは、Kafkaデータを消費する従来の方法です。この方法をWALメカニズムと組み合わせると、データ損失なしで高い信頼性を確保できますが、データが1回だけ処理されることを保証することはできず、2回処理される可能性があります。受信者は定期的にオフセットをzkに送信するため、SparkとZooKeeperは同期されない可能性があります。つまり、zkには0 1024のオフセットがあり、sparkが同期されると01500になる可能性があります。同期中に再同期される消費される

直接的なアプローチに基づいて、Kafkaの単純なAPIを使用して、Spark Streaming自体が消費オフセットを追跡し、チェックポイントに保存します。Spark自体を同期する必要があるため、データが1回だけ消費されることが保証されます。

package com.stream

import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.SparkConf
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.{
    
    Seconds, StreamingContext}
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe


object StreamFromKafka {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val conf = new SparkConf().setAppName("StreamWordCount").setMaster("local[2]")
    val sc = new StreamingContext(conf,Seconds(10))

    val kafkaParams = Map[String, Object](
      "bootstrap.servers" -> "192.168.182.146:9092,192.168.182.147:9092,192.168.182.148:9092",
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      "group.id" -> "group1"
    )

    /**
      * LocationStrategies.PreferBrokers() 仅仅在你 spark 的 executor 在相同的节点上,优先分配到存在  kafka broker 的机器上;
      * LocationStrategies.PreferConsistent(); 大多数情况下使用,一致性的方式分配分区所有 executor 上。(主要是为了分布均匀)
      * 新的Kafka使用者API将预先获取消息到缓冲区。因此,出于性能原因,Spark集成将缓存的消费者保留在执行程序上(而不是为每个批处理重新创建它们),并且更喜欢在具有适当使用者的主机位置上安排分区,这一点很重要。
      *在大多数情况下,您应该使用LocationStrategies.PreferConsistent,如上所示。这将在可用执行程序之间均匀分配分区。如果您的执行程序与Kafka代理在同一主机上,请使用PreferBrokers,它更愿意为该分区安排Kafka领导者的分区。
      */
    val topics = Array("test")
    val stream = KafkaUtils.createDirectStream[String, String](
      sc,
      PreferConsistent,
      Subscribe[String, String](topics, kafkaParams)
    )
    val kafkaStream = stream.map(record => (record.key, record.value))
    val words = kafkaStream.map(_._2)
    val pairs = words.map {
    
     x => (x,1) }
    val wordCounts = pairs.reduceByKey(_+_)
    wordCounts.print()
    sc.start()
    sc.awaitTermination()
  }

}

おすすめ

転載: blog.csdn.net/dudadudadd/article/details/114402955
おすすめ