• StructuredStreaming整合kafka


    一、版本配置

    scala: 2.11.8

    spark: 2.3.4

    kafka: 2.11-0.10.0.0

    二、pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.test</groupId>
        <artifactId>StructuredStreaming</artifactId>
        <version>1.0-SNAPSHOT</version>
    
    
        <properties>
            <scala.version>2.11.8</scala.version>
            <spark.version>2.3.4</spark.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.scala-lang</groupId>
                <artifactId>scala-library</artifactId>
                <version>${scala.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-streaming_2.11</artifactId>
                <version>${spark.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-sql-kafka-0-10_2.11</artifactId>
                <version>${spark.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-sql_2.11</artifactId>
                <version>${spark.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-core_2.11</artifactId>
                <version>${spark.version}</version>
            </dependency>
            <dependency>
                <groupId>net.jpountz.lz4</groupId>
                <artifactId>lz4</artifactId>
                <version>1.3.0</version>
            </dependency>
        </dependencies>
    
    
        <build>
            <!--指定打包的位置,默认只打src/main/java目录,且只能打包一个目录-->
            <sourceDirectory>src/main/scala</sourceDirectory>
            <testSourceDirectory>src/test/scala</testSourceDirectory>
            <plugins>
                <!--指定java版本-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
    
    
                <!--scala依赖插件,为scala提供支持-->
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>scala-maven-plugin</artifactId>
                    <version>3.2.0</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>testCompile</goal>
                            </goals>
                            <configuration>
                                <args>
                                    <arg>-dependencyfile</arg>
                                    <arg>${project.build.directory}/.scala_dependencies</arg>
                                </args>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
    
                <!--把所有jar包集成到一个jar包中-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.1.1</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <!--去掉META-INF文件中可能出现的非法签名文件-->
                                <filters>
                                    <filter>
                                        <artifact>*:*</artifact>
                                        <excludes>
                                            <exclude>META-INF/*.SF</exclude>
                                            <exclude>META-INF/*.DSA</exclude>
                                            <exclude>META-INF/*.RSA</exclude>
                                        </excludes>
                                    </filter>
                                </filters>
                                <transformers>
                                    <transformer
                                            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <mainClass>com.test.structuredstreaming.socket.SocketWordCount</mainClass>
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    
    </project>
    View Code

    三、读取kafka的数据

    1. 以流的形式读取

    1)读取一个topic

    val df = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("subscribe", "topic1")
      .load()
    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .as[(String, String)]

    2)读取多个topic

    val df = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("subscribe", "topic1,topic2")
      .load()
    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .as[(String, String)]

    3)读取通配符形式的topic组

    val df = spark
      .readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("subscribePattern", "topic.*")
      .load()
    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .as[(String, String)]

    2.以批的形式读取

    1)设置每个分区的起始和结束值

    val df = spark
      .read
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("subscribe", "topic1,topic2")
      .option("startingOffsets", """{"topic1":{"0":23,"1":-2},"topic2":{"0":-2}}""")
      .option("endingOffsets", """{"topic1":{"0":50,"1":-1},"topic2":{"0":-1}}""")
      .load()
    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .as[(String, String)]

    2)配置起始和结束的offset值(默认)

    val df = spark
      .read
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("subscribePattern", "topic.*")
      .option("startingOffsets", "earliest")
      .option("endingOffsets", "latest")
      .load()
    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .as[(String, String)]

    3.Schema信息

    读取后的数据的Schema是固定的,包含的列如下:

    column type 说明
    key binary 信息的key
    value binary 信息的value(我自己的数据)
    topic string 主题
    partition int 分区
    offset long 偏移值
    timestamp long 时间戳
    timestampType int 类型

    四、source相关配置

    kafka.bootstrap.servers  kafka的服务器配置,host:post形式,用逗号进行分割,如host1:9000,host2:9000

    startingOffsets  offset开始的值,如果是earliest,则从最早的数据开始读;如果是latest,则从最新的数据开始读。默认流是latest,批是earliest

    endingOffsets  最大的offset,只在批处理的时候设置,如果是latest则为最新的数据

    failOnDataLoss  在流处理时,当数据丢失时(比如topic被删除了,offset在指定的范围之外),查询是否报错,默认为true。这个功能可以当做是一种告警机制,如果对丢失数据不感兴趣,可以设置为false。在批处理时,这个值总是为true。

    kafkaConsumer.pollTimeoutMs  excutor连接kafka的超时时间,默认是512ms

    fetchOffset.numRetries  获取kafka的offset信息时,尝试的次数;默认是3次

    fetchOffset.retryIntervalMs  尝试重新读取kafka offset信息时等待的时间,默认是10ms

    maxOffsetsPerTrigger  单个批次允许抓取的最大消息条数

    五、写入数据到kafka

    Apache kafka仅支持“至少一次”的语义,因此,无论是流处理还是批处理,数据都有可能重复。比如,当出现失败的时候,structured streaming会尝试重试,但是不会确定broker那端是否已经处理以及持久化该数据。但是如果query成功,那么可以断定的是,数据至少写入了一次。比较常见的做法是,在后续处理kafka数据时,再进行额外的去重,关于这点,其实structured streaming有专门的解决方案。

    保存数据时的schema:

    1) key,可选。如果没有填,那么key会当做null,kafka针对null会有专门的处理(待查)。

    2) value,必须有

    3) topic,可选。(如果配置option里面有topic会覆盖这个字段)

    下面是sink输出必须要有的参数:

    kafka.bootstrap.servers,kafka的集群地址,host:port格式用逗号分隔。

    流处理的数据写入

    // 基于配置指定topic
    val ds = df
      .selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .writeStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("topic", "topic1")
      .start()
    
    // 在字段中包含topic
    val ds = df
      .selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
      .writeStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .start()

    批处理的数据写入

    df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
      .write
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .option("topic", "topic1")
      .save()
    
    df.selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
      .write
      .format("kafka")
      .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
      .save()

    六、kafka特殊配置

    针对Kafka的特殊处理,可以通过DataStreamReader.option进行设置。

    关于(详细的kafka配置可以参考consumer的官方文档](http://kafka.apache.org/documentation.html#newconsumerconfigs)

    以及kafka producer的配置

    请注意,无法设置以下Kafka参数,并且Kafka源或接收器将引发异常:

    1)group.id  Kafka source将为每个查询自动创建一个唯一的组id。用户可以设置自动生成的组.id通过可选的源选项groupIdPrefix,默认值为“spark kafka source”。您也可以设置“kafka.group.id“但是,要强制Spark使用特殊组id,请阅读此选项的警告并谨慎使用。

    2)auto.offset.reset  设置源选项startingOffsets以指定起始位置。结构化流媒体管理内部消费的补偿,而不是依靠kafka消费者来完成。这将确保在动态订阅新主题/分区时不会丢失任何数据。请注意,startingoffset仅在新的流式查询启动时适用,并且继续将始终从查询停止的位置开始。

    3)key.deserializervalue.deserializerkey.serializervalue.serializer 序列化与反序列化,都是ByteArraySerializer

    4)enable.auto.commit  Kafka源不提交任何偏移量。

    5)interceptor.classes  Kafka源代码总是以字节数组的形式读取密钥和值。使用ConsumerInterceptor是不安全的,因为它可能会中断查询。

     七、代码

    package com.test.structuredstreaming.kafka
    
    import org.apache.spark.sql.streaming.Trigger
    import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
    
    /**
      *
      * @author c
      * @date 2020/8/17 16:54 
      *
      */
    object kafka_demo2 {
    
      def main(args: Array[String]): Unit = {
        // 准备环境
        val spark: SparkSession = SparkSession.builder()
          .appName("demo03")
          .master("local[2]")
          .getOrCreate()
    
        // 设置日志级别
        spark.sparkContext.setLogLevel("ERROR")
    
        //  导入隐式转换
        import spark.implicits._
    
        // 读取数据流中的数据
        val kafkaDatas: DataFrame = spark.readStream
          .format("kafka")
          .option("kafka.bootstrap.servers", "192.168.100.110:9092") // 设置kafka集群
          .option("group.id", "2") //消费组
          .option("subscribe", "test") // 设置需要读取的主题topic
          .option("startingOffsets", "latest")// 消费最新的数据
          .option("maxOffsetsPerTrigger", 3)//单个批次允许抓取的最大消息条数
          .load() // 加载数据
    
        // kafkaDatas  内部数据是kafka数据(key,value)
        val kafkaDataString: Dataset[(String, String)] = kafkaDatas.selectExpr("CAST(key AS string)", "CAST(value AS string)").as[(String, String)]
    
        // 处理,将数据按照空格切分
        val word: Dataset[String] = kafkaDataString.flatMap(x => x._2.split(" "))
    
        // 利用DSL语句对数据进行wordcount
        val wordCount: Dataset[Row] = word.groupBy("value").count().sort($"count".desc)
    
        // 输出
        wordCount.writeStream
          .outputMode("complete") // 每次将所有的数据写出
          .format("console") // 输出方式,console表示控制台
          .trigger(Trigger.ProcessingTime(0))//触发时间间隔,0表示尽可能的快
          .start() // 开启任务
          .awaitTermination() // 等待程序停止
    
    
      }
    }
  • 相关阅读:
    kafka+zookeeper集群部署
    rabbitmq集群部署
    nginx location语法
    rabbitmq单一部署
    Centos6国内可用yum源
    css
    imutable
    js解构复制语法
    redux
    json server问题
  • 原文地址:https://www.cnblogs.com/chong-zuo3322/p/13524671.html
Copyright © 2020-2023  润新知