Dos formas en que SparkStreaming consume Kafka

Necesidad de importar dependencias pom

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

Uno, la forma de leer del receptor

De esta forma, Receiver se utiliza para obtener datos. Receiver utiliza la API de consumidor de alto nivel de Kafka para lograr el consumo de datos.

Los datos obtenidos de Kafka por el receptor se almacenan en la memoria de Spark Executor, y luego el trabajo iniciado por Spark Streaming procesará los datos.

Sin embargo, en la configuración predeterminada, este método puede perder datos debido a la falla de la capa subyacente, porque la API avanzada de Kafka no mantendrá el desplazamiento durante el consumo.

Para habilitar un mecanismo altamente confiable y cero pérdida de datos, habilite el mecanismo Write Ahead Log (WAL) de Spark Streaming. Este mecanismo escribirá sincrónicamente los datos de Kafka recibidos en el registro de escritura anticipada en el sistema de archivos distribuido (como HDFS). Por lo tanto, incluso si falla la tarea de chispa, los datos del registro de escritura anticipada se pueden usar para la recuperación.

Puntos a tener en cuenta

1. De esta forma, la partición del tema consumido en Kafka no tiene nada que ver con la partición del RDD en Spark. Por lo tanto, en KafkaUtils.createStream (), la cantidad de particiones que aumentamos solo aumentará la cantidad de subprocesos para que Receiver lea kafka_partition. No aumentará el paralelismo de los datos de procesamiento de Spark.

2. Puede crear múltiples DStreams de entrada de Kafka y utilizar diferentes grupos de consumidores y temas para recibir datos en paralelo a través de múltiples receptores.

3. Si un sistema de archivos tolerante a fallas, como HDFS, tiene habilitado un mecanismo de registro de escritura anticipada, los datos recibidos se copiarán en el registro de escritura anticipada. Por lo tanto, en KafkaUtils.createStream (), el nivel de persistencia establecido es StorageLevel.MEMORY_AND_DISK_SER

4. KafkaUtils ha eliminado la api leída por el receptor en la versión más reciente de jar

Aunque se ha eliminado, todavía necesitamos saber cómo implementarlo:

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)
      })
    })
  }
}

En segundo lugar, lea en modo directo

Este nuevo método directo que no se basa en Receiver se introdujo en Spark 1.3 para garantizar un mecanismo más robusto. En lugar de utilizar Receiver para recibir datos, este método consultará periódicamente a Kafka para obtener el último desplazamiento de cada tema + partición, definiendo así el rango de desplazamiento para cada lote. Cuando se inicia un trabajo que procesa datos, la API de consumidor simple de Kafka se utilizará para obtener datos en el rango de compensación especificado de Kafka.

Este método tiene las siguientes ventajas:

1. Simplifique la lectura en paralelo. Si desea leer múltiples particiones, no necesita crear múltiples DStreams de entrada y luego realizar operaciones de unión en ellos. Spark creará tantas particiones RDD como particiones Kafka y leerá datos de Kafka en paralelo. Por lo tanto, existe una relación de mapeo uno a uno entre la partición Kafka y la partición RDD.

2. Alto rendimiento: si desea garantizar una pérdida de datos cero, debe activar el mecanismo WAL en el método basado en receptor. Este método es en realidad ineficaz, porque los datos se copian dos veces. Kafka tiene un mecanismo altamente confiable para copiar una copia de los datos y luego copiar una copia al WAL. Basado en el método directo, no depende de Receiver y no necesita activar el mecanismo WAL. Siempre que los datos estén altamente disponibles en Kafka, se pueden restaurar a través de la copia de Kafka.

3. Mecanismo de transacción única y única:

Según el método del receptor, la API de alto nivel de Kafka se utiliza para guardar la compensación consumida en ZooKeeper. Esta es la forma tradicional de consumir datos de Kafka. Este método combinado con el mecanismo WAL puede garantizar una alta confiabilidad con cero pérdida de datos, pero no puede garantizar que los datos se procesen una sola vez y que puedan procesarse dos veces. Debido a que el receptor enviará periódicamente el desplazamiento a zk, es posible que Spark y ZooKeeper no estén sincronizados, es decir, puede haber un desplazamiento de 0 1024 en zk, y puede ser 0 1500 cuando se sincroniza Spark, entonces 1023 1500 lo hará. volver a sincronizarse durante la sincronización Consumirse

Basado en el enfoque directo, utilizando la API simple de Kafka, Spark Streaming es responsable de rastrear la compensación de consumo y almacenarla en el punto de control. Spark en sí debe estar sincronizado, por lo que puede garantizar que los datos se consuman una vez y solo una vez.

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()
  }

}

Supongo que te gusta

Origin blog.csdn.net/dudadudadd/article/details/114402955
Recomendado
Clasificación