Spark实时(六):Output Sinks案例演示

文章目录

[Output Sinks案例演示](#Output Sinks案例演示)

[一、​​​​​​​File sink](#一、File sink)

[二、​​​​​​​​​​​​​​Memory Sink](#二、Memory Sink)

[三、​​​​​​​​​​​​​​Foreach Sink](#三、Foreach Sink)

1、​​​​​​​foreachBatch

2、​​​​​​​​​​​​​​foreach


Output Sinks案例演示

当我们对流式数据处理完成之后,可以将数据写出到Flie、Kafka、console控制台、memory内存,或者直接使用foreach做个性化处理。关于将数据结果写出到Kafka在StructuredStreaming与Kafka整合部分再详细描述。

对于一些可以保证端到端容错的sink输出,需要指定checkpoint目录来写入数据信息,指定的checkpoint目录可以是HDFS中的某个路径,设置checkpoint可以通过SparkSession设置也可以通过DataStreamWriter设置,设置方式如下:

Scala 复制代码
//通过SparkSession设置checkpoint
spark.conf.set("spark.sql.streaming.checkpointLocation","hdfs://mycluster/checkpintdir")

或者

//通过DataStreamWriter设置checkpoint
df.writeStream
  .format("xxx")
  .option("checkpointLocation","./checkpointdir")
  .start()

checkpoint目录中会有以下目录及数据:

  • **offsets:**记录偏移量目录,记录了每个批次的偏移量。
  • **commits:**记录已经完成的批次,方便重启任务检查完成的批次与offset批次做对比,继续offset消费数据,运行批次。
  • **metadata:**metadata元数据保存jobid信息。
  • **sources:**数据源各个批次读取详情。
  • **sinks:**数据sink写出批次情况。
  • **state:**记录状态值,例如:聚合、去重等场景会记录相应状态,会周期性的生成snapshot文件记录状态。

下面对File、memoery、foreach output Sink进行演示。

一、​​​​​​​​​​​​​​File sink

Flie Sink就是数据结果实时写入到执行目录下的文件中,每次写出都会形成一个新的文件,文件格式可以是parquet、orc、json、csv格式。

Scala代码如下:

Scala 复制代码
package com.lanson.structuredStreaming.sink

import org.apache.spark.sql.streaming.StreamingQuery
import org.apache.spark.sql.{DataFrame, SparkSession}

/**
  *  读取Socket数据,将数据写入到csv文件
  */
object FileSink {

  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder().master("local")
      .appName("File Sink")
      .config("spark.sql.shuffle.partitions", 1)
      .getOrCreate()

    val result: DataFrame = spark.readStream
      .format("socket")
      .option("host", "node3")
      .option("port", 9999)
      .load()

    val query: StreamingQuery = result.writeStream
      .format("csv")
      .option("path", "./dataresult/csvdir")
      .option("checkpointLocation","./checkpint/dir3")
      .start()
    query.awaitTermination()

  }
}

​​​​​​​

在socket中输入数据之后,每批次数据写入到一个csv文件中。

二、​​​​​​​​​​​​​​Memory Sink

memory Sink是将结果作为内存表存储在内存中,支持Append和Complete输出模式,这种结果写出到内存表方式多用于测试,如果数据量大要慎用。另外查询结果表中数据时需要写一个循环每隔一段时间读取内存中的数据。

Scala代码如下:

sql 复制代码
package com.lanson.structuredStreaming.sink

import org.apache.spark.sql.{DataFrame, SparkSession}
import org.apache.spark.sql.streaming.StreamingQuery

/**
  *  读取scoket 数据写入memory 内存,再读取
  */
object MemorySink {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder().master("local")
      .appName("Memory Sink")
      .config("spark.sql.shuffle.partitions", 1)
      .getOrCreate()

    spark.sparkContext.setLogLevel("Error")

    val result: DataFrame = spark.readStream
      .format("socket")
      .option("host", "node3")
      .option("port", 9999)
      .load()


    val query: StreamingQuery = result.writeStream
      .format("memory")
      .queryName("mytable")
      .start()

    //查询内存中表数据
    while(true){
      Thread.sleep(2000)
      spark.sql(
        """
          |select * from mytable
        """.stripMargin).show()
    }

    query.awaitTermination()
  }

}

三、​​​​​​​​​​​​​​Foreach Sink

foreach 可以对输出的结果数据进行自定义处理逻辑,针对结果数据自定义处理逻辑数据除了有foreach之外还有foreachbatch,两者区别是foreach是针对一条条的数据进行自定义处理,foreachbatch是针对当前小批次数据进行自定义处理。

1、​​​​​​​foreachBatch

foreachBatch可以针对每个批次数据进行自定义处理,该方法需要传入一个函数,函数有2个参数,分别为当前批次数据对应的DataFrame和当前batchId。

案例:实时读取socket数据,将结果批量写入到mysql中。

Scala代码如下:

Scala 复制代码
package com.lanson.structuredStreaming.sink

import org.apache.spark.sql.streaming.StreamingQuery
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}

/**
  *  读取Socket 数据,将数据写出到mysql中
  */
object ForeachBatchTest {

  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder().appName("ForeachBatch Sink")
      .master("local")
      .config("spark.sql.shuffle.partitions", 1)
      .getOrCreate()


    import spark.implicits._

    val df: DataFrame = spark.readStream
      .format("socket")
      .option("host", "node2")
      .option("port", 9999)
      .load()

    val personDF: DataFrame = df.as[String].map(line => {
      val arr: Array[String] = line.split(",")
      (arr(0).toInt, arr(1), arr(2).toInt)
    }).toDF("id", "name", "age")

    val query: StreamingQuery = personDF.writeStream
      .foreachBatch((batchDF: DataFrame, batchId: Long) => {
        println("batchID : " + batchId)
        batchDF.write.mode(SaveMode.Append).format("jdbc")
          .option("url","jdbc:mysql://node3:3306/testdb?useSSL=false")
          .option("user","root")
          .option("password","123456")
          .option("dbtable","person")
          .save()
      }).start()
    query.awaitTermination();


  }

}

运行结果:

Java代码如下:

java 复制代码
package com.lanson.structuredStreaming.sink;

import java.util.concurrent.TimeoutException;
import org.apache.spark.api.java.function.MapFunction;
import org.apache.spark.api.java.function.VoidFunction2;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Encoders;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SaveMode;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.streaming.StreamingQueryException;
import scala.Tuple3;

public class ForeachBatchTest01 {
    public static void main(String[] args) throws TimeoutException, StreamingQueryException {
        SparkSession spark = SparkSession.builder().master("local")
                .appName("ForeachBatchTest01")
                .config("spark.sql.shuffle.partitions", 1)
                .getOrCreate();
        spark.sparkContext().setLogLevel("Error");

        Dataset<Row> result = spark.readStream()
                .format("socket")
                .option("host", "node2")
                .option("port", 9999)
                .load()
                .as(Encoders.STRING())
                .map(new MapFunction<String, Tuple3<Integer, String, Integer>>() {
                    @Override
                    public Tuple3<Integer, String, Integer> call(String line) throws Exception {
                        String[] arr = line.split(",");
                        return new Tuple3<>(Integer.valueOf(arr[0]), arr[1], Integer.valueOf(arr[2]));
                    }
                }, Encoders.tuple(Encoders.INT(), Encoders.STRING(), Encoders.INT()))
                .toDF("id", "name", "age");

        result.writeStream()
                .foreachBatch(new VoidFunction2<Dataset<Row>, Long>() {
                    @Override
                    public void call(Dataset<Row> df, Long batchId) throws Exception {
                        System.out.println("batchID : "+batchId);
                        //将df 保存到mysql
                        df.write().format("jdbc")
                                .mode(SaveMode.Append)
                                .option("url","jdbc:mysql://node3:3306/testdb?useSSL=false" )
                                .option("user","root" )
                                .option("password","123456" )
                                .option("dbtable","person" )
                                .save();
                    }
                }).start()
                .awaitTermination();

    }
}

运行结果:

在mysql中创建testdb库,并创建person表,这里也可以不创建表:

sql 复制代码
create database testdb;
create table person(id int(10),name varchar(255),age int(2));
sql 复制代码
1,zs,18
2,ls,19
3,ww,20
4,ml,21
5,tq,22
6,ll,29

mysql结果如下:

2、​​​​​​​​​​​​​​foreach

foreach可以针对数据结果每条数据进行处理。

案例:实时读取socket数据,将结果一条条写入到mysql中。

Scala代码如下:

Scala 复制代码
package com.lanson.structuredStreaming.sink

import java.sql.{Connection, DriverManager, PreparedStatement}

import org.apache.spark.sql.execution.streaming.sources.ForeachWrite
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}

object ForeachSinkTest {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder().appName("ForeachBatch Sink")
      .master("local")
      .config("spark.sql.shuffle.partitions", 1)
      .getOrCreate()

    spark.sparkContext.setLogLevel("Error")

    import spark.implicits._

    val df: DataFrame = spark.readStream
      .format("socket")
      .option("host", "node2")
      .option("port", 9999)
      .load()

    val personDF: DataFrame = df.as[String].map(line => {
      val arr: Array[String] = line.split(",")
      (arr(0).toInt, arr(1), arr(2).toInt)
    }).toDF("id", "name", "age")

    personDF.writeStream
      .foreach(new ForeachWriter[Row]() {
        var  conn: Connection  = _
        var pst: PreparedStatement = _
        //打开资源
        override def open(partitionId: Long, epochId: Long): Boolean = {
          conn = DriverManager.getConnection("jdbc:mysql://node3:3306/testdb?useSSL=false","root","123456")
          pst = conn.prepareStatement("insert into person values (?,?,?)")
          true
        }

        //一条条处理数据
        override def process(row: Row): Unit = {
          val id: Int = row.getInt(0)
          val name: String = row.getString(1)
          val age: Int = row.getInt(2)
          pst.setInt(1,id)
          pst.setString(2,name)
          pst.setInt(3,age)
          pst.executeUpdate()
        }

        //关闭释放资源
        override def close(errorOrNull: Throwable): Unit = {
          pst.close()
          conn.close()
        }
      }).start()
      .awaitTermination()
  }

}

运行结果:

Java代码如下:

java 复制代码
package com.lanson.structuredStreaming.sink;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.TimeoutException;
import org.apache.spark.api.java.function.MapFunction;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Encoders;
import org.apache.spark.sql.ForeachWriter;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.streaming.StreamingQueryException;
import scala.Tuple3;

public class ForeachSinkTest01 {
    public static void main(String[] args) throws TimeoutException, StreamingQueryException {
        SparkSession spark = SparkSession.builder().master("local")
                .appName("SSReadSocketData")
                .config("spark.sql.shuffle.partitions", 1)
                .getOrCreate();
        spark.sparkContext().setLogLevel("Error");

        Dataset<Row> result = spark.readStream()
                .format("socket")
                .option("host", "node2")
                .option("port", 9999)
                .load()
                .as(Encoders.STRING())
                .map(new MapFunction<String, Tuple3<Integer, String, Integer>>() {
                    @Override
                    public Tuple3<Integer, String, Integer> call(String line) throws Exception {
                        String[] arr = line.split(",");
                        return new Tuple3<>(Integer.valueOf(arr[0]), arr[1], Integer.valueOf(arr[2]));
                    }
                }, Encoders.tuple(Encoders.INT(), Encoders.STRING(), Encoders.INT()))
                .toDF("id", "name", "age");

        result.writeStream()
                .foreach(new ForeachWriter<Row>() {
                    Connection conn;
                    PreparedStatement pst ;
                    @Override
                    public boolean open(long partitionId, long epochId) {
                        try {
                            conn = DriverManager.getConnection("jdbc:mysql://node3:3306/testdb?useSSL=false", "root", "123456");
                            pst = conn.prepareStatement("insert into person values (?,?,?)");
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                        return true;
                    }
                    @Override
                    public void process(Row row) {
                        int id = row.getInt(0);
                        String name = row.getString(1);
                        int age = row.getInt(2);
                        try {
                            pst.setInt(1,id );
                            pst.setString(2,name );
                            pst.setInt(3,age );
                            pst.executeUpdate();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }


                    }

                    @Override
                    public void close(Throwable errorOrNull) {
                        try {
                            pst.close();
                            conn.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }


                }).start().awaitTermination();
    }
}

运行

以上代码编写完成后,清空mysql person表数据,然后输入以下数据:

sql 复制代码
1,zs,18
2,ls,19
3,ww,20
4,ml,21
5,tq,22
6,ll,29
1,zs,18
2,ls,19
3,ww,20
4,ml,21
5,tq,22
6,ll,29

mysql结果如下:


  • 📢博客主页:https://lansonli.blog.csdn.net
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 Lansonli 原创,首发于 CSDN博客🙉
  • 📢停下休息的时候不要忘了别人还在奔跑,希望大家抓紧时间学习,全力奔赴更美好的生活✨
相关推荐
Elastic 中国社区官方博客42 分钟前
如何将数据从 AWS S3 导入到 Elastic Cloud - 第 3 部分:Elastic S3 连接器
大数据·elasticsearch·搜索引擎·云计算·全文检索·可用性测试·aws
Aloudata2 小时前
从Apache Atlas到Aloudata BIG,数据血缘解析有何改变?
大数据·apache·数据血缘·主动元数据·数据链路
不能再留遗憾了2 小时前
RabbitMQ 高级特性——消息分发
分布式·rabbitmq·ruby
水豚AI课代表2 小时前
分析报告、调研报告、工作方案等的提示词
大数据·人工智能·学习·chatgpt·aigc
茶馆大橘2 小时前
微服务系列六:分布式事务与seata
分布式·docker·微服务·nacos·seata·springcloud
材料苦逼不会梦到计算机白富美5 小时前
golang分布式缓存项目 Day 1
分布式·缓存·golang
拓端研究室TRL5 小时前
【梯度提升专题】XGBoost、Adaboost、CatBoost预测合集:抗乳腺癌药物优化、信贷风控、比特币应用|附数据代码...
大数据
黄焖鸡能干四碗5 小时前
信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料word)
大数据·人工智能·软件需求·设计规范·规格说明书
想进大厂的小王5 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构
Java 第一深情5 小时前
高性能分布式缓存Redis-数据管理与性能提升之道
redis·分布式·缓存