flink算子之sink算子

sink算子: 把计算之后的最终结果写入到某个位置

1.文件

1)writeAsText/ writeAsCsv

弃用的方法,但是可以使用

如果并行度为1,直接以一个文件的形式体现,内容是追加的,因为设置了并行度env.setParallelism(1)

如果并行度大于1,以一个目录的形式出现,并行度是几就有几个文件,不设置并行度时,CPU的核数为并行度数目

dataStreamOriginal.writeAsText("E://path")

2) addSink(StreamingFileSink)

StreamingFileSink.forRowFormat获取内部类RowFormatBuilder的对象,内部类RowFormatBuilder的对象调用build()获取到单利SimpleStringEncoder

目前建议使用的方法,不管并行度是多少,都以目录形式出现

dataStreamOriginal.addSink(StreamingFileSink.forRowFormat(
    new Path("E://path"),
    new SimpleStringEncoder[SensorReading]()
 ).build()

 

/**
   * 把结果写入到文件,下沉到sink
   * @param dataStreamOriginal
   */
  def sink_File(dataStreamOriginal: DataStream[SensorReading]): Unit ={
    //文件:E://path 只有一个分区,因为设置了并行度env.setParallelism(1)
    //不设置并行度时,CPU的核数为并行度数目,此时路径为目录级别,文件数为CPU核数
    //分区:则是以目录形式,每一个分区产生一个文件

    //被弃用的写入文件的方法
    dataStreamOriginal.writeAsText("E://path")
    //dataStreamOriginal.writeAsCsv()

    //官方建议我们写入文件使用的方法
    dataStreamOriginal.addSink(
      //StreamingFileSink.forRowFormat获取内部类RowFormatBuilder的对象
      //内部类RowFormatBuilder的对象调用build()获取到单利SimpleStringEncoder
      StreamingFileSink.forRowFormat(
        new Path("E://path"),
        new SimpleStringEncoder[SensorReading]()
      ).build()
    )
  }
def main(args: Array[String]): Unit = {
    //创建实时流处理的环境
    val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置全局并行度为1
    env.setParallelism(1)
    //创建原始数据流
    val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
    //直接把字符串的流转成sensorReading的流
    val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
    sink_File(dataStream_sensorReading)
    //实时数据流必须设置开启启动执行的语句
    env.execute("transform")
  }

2. socket

由flink本身提供的方法,本质也是addSink

流程:

1)把数据下沉到socket   从8888端口获取原数据,下沉到9999端口

2)telnet -> 控制台 -> 服务端

注意:在socket上接收的数据基本上都是字符串(String)

/**
   * 把数据下沉到socket
   * 从8888端口获取原数据,下沉到9999端口
   * @param dataStreamOriginal
   */
  def sink_Socket(dataStreamOriginal: DataStream[String]): Unit ={
    //服务端:nc -lk 8888
    //控制台:客户端
    //服务端:nc -lk 9999
    //telnet -> 控制台 -> 服务端
    //Socket接收的数据类型为String,使用的是最原始的数据
    dataStreamOriginal.writeToSocket(
      "node160",
      9999,
      new SimpleStringSchema()
    )
  }
def main(args: Array[String]): Unit = {
    //创建实时流处理的环境
    val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置全局并行度为1
    env.setParallelism(1)
    //创建原始数据流
    val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
    //直接把字符串的流转成sensorReading的流
    val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
    //向socket送数据应该都是String,把最原始的数据送出去
    sink_Socket(dataStream_Original)

    //实时数据流必须设置开启启动执行的语句
    env.execute("transform")
  }

 3.kafka

      有第三方jar包提供的类,通过addSink添加

流程:代码(把数据下沉到kafka中 ,充当kafka的生产者)当前kafka生产者的构造方法,是弃用的,但是比较简单

服务器发送数据 -> 控制台下沉 -> kafka生产者进行处理 -> kafka消费者进行消费

前提步骤:1).开启zookeeper    2).开启kafka服务  3).开启kafka的消费者   4).nc -lk 8888

/**
   * 把数据下沉到kafka中
   * 充当kafka的生产者
   * @param dataStreamOriginal
   */
  def sink_kafka(dataStreamOriginal:DataStream[String]) = {
    //当前kafka生产者的构造方法,是弃用的,但是比较简单
    //1.开启zookeeper
    //2.开启kafka服务
    //3.开启kafka的消费者
    //4.nc -lk 8888
    //服务器发送数据 -> 控制台下沉 -> kafka生产者进行处理 -> kafka消费者进行消费
    dataStreamOriginal.addSink(new FlinkKafkaProducer[String](
      "node160:9092",
      "myTopic",
      new SimpleStringSchema()
    ))
  }
 def main(args: Array[String]): Unit = {
    //创建实时流处理的环境
    val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置全局并行度为1
    env.setParallelism(1)
    //创建原始数据流
    val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
    //直接把字符串的流转成sensorReading的流
    val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)

    sink_kafka(dataStream_Original)

    //实时数据流必须设置开启启动执行的语句
    env.execute("transform")
  }

4.自定义

自己定义一个类,继承 [Rich]SinkFunction,重写 invoke 方法

 流程:把数据下沉到mysql数据表中,flink本身没有提供此方法的下沉方式,需要自定义下沉逻辑,继承需要继承RichSinkFunction 开启执行一次连接,最后关闭一次连接,所以此逻辑需要使用 富函数

需求: 从socket读取SenorRending的数据,id相同做update操作,id不同做insert操作

/**
 * 自定义下沉到mysql的逻辑
 * 继承需要继承RichSinkFunction 开启执行一次连接,最后关闭一次连接
 * 当前逻辑下需要开始的时候连接一次数据库和关闭一次数据库
 * 所以此逻辑需要使用 富函数
 */
class MySinkToMysql extends RichSinkFunction[SensorReading]{

  var conn:Connection = null
  var ps_update:PreparedStatement = null
  var ps_insert:PreparedStatement = null

  /**
   * 开始只执行一次的方法,用来做数据库连接
   * @param parameters
   */
  override def open(parameters: Configuration): Unit = {
    //java驱动
    Class.forName("com.mysql.jdbc.Driver")
    //连接mysql
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/flink","root","1234")
    //对数据进行修改数据
    ps_update = conn.prepareStatement(" update sensor_temp set temp=? where id=? ")
    //插入数据
    ps_insert = conn.prepareStatement(" insert into sensor_temp value(?,?)")
  }

  /**
   * 对于每条数据库的操作
   * @param value
   */
  override def invoke(value: SensorReading,context:SinkFunction.Context[_]): Unit = {
   //首先进行修改数据
    ps_update.setDouble(1,value.temperature)
    ps_update.setString(2,value.id)
    //如果修改成功的话,updateCount>0
    val updateCount = ps_update.executeUpdate()

    //updateCount=0,表示没有插入成功
    if(updateCount==0){
      ps_insert.setString(1,value.id)
      ps_insert.setDouble(2,value.temperature)
      //insertCount>0表示插入成功
      val insertCount = ps_insert.executeUpdate()
      if(insertCount==1){
        println("id不存在,添加成功")
      }
    }else if(updateCount==1){
      println("id存在,更新成功")
    }
  }

  /**
   * 最后只执行一次的方法
   * 用于数据库的连接
   * @return
   */
  override def clone():Unit = {
    if(null!=ps_insert){
      ps_insert.close()
    }
    if(null!=ps_update){
      ps_update.close()
    }
    if(null!=conn){
      conn.close()
    }
  }
}
def main(args: Array[String]): Unit = {
    //创建实时流处理的环境
    val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置全局并行度为1
    env.setParallelism(1)
    //创建原始数据流
    val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
    //直接把字符串的流转成sensorReading的流
    val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
)
    sink_MySql(dataStream_sensorReading)

    //实时数据流必须设置开启启动执行的语句
    env.execute("transform")
  }

 

你可能感兴趣的:(flink,大数据,big,data)