Flink的Source端和Sink端大全

在这里插入图片描述

enviroment

  1. getExecutionEnvironment

          创建一个执行环境,表示当前执行程序的上下文。如果程序是独立调的,则此方法返回本地执行文件;如果从命令行客户端调用程序以提交到集群,则次方法返回此集群的环境,也就是说,getExecutionEnvironment 会根据查询运行的方式决定返回什么样的运行环境,是最常用的一种创建执行环境的方式。

    val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
    

          如果设置并行度,会以 flink-conf.vaml 中的配置为准,默认为 1。

    在这里插入图片描述

  2. createLocalEnvironment

          返回本地执行环境,需要在调用时指定默认的并行度。

    val env = StreamExecutionEnvironment.createLocalEnvironment(1)
    
  3. createRemoteEnvironment

          返回集群执行环境,将 Jar 提交到远程服务器。需要在调用时指定 JobManager 的 IP 和端口号,并指定要在集群中运行的 Jar 包。

    val env = ExecutionEnvironment.createRemoteEnvironment("jobmanager-hostname", 6123,"C://jar//flink//wordcount.jar")
    

Source

flink + kafka (flink 消费 kafka 中的数据)

  1. 启动 zk,kafka。

  2. 创建 topic,以及启动生产者。

    1 bin/kafka-topics.sh --create --partitions 3 --replication-factor 2 --topic testnew --bootstrap-server vm0:2181,vm1:2181,vm2:2181
    2 bin/kafka-console-producer.sh --broker-list vm0:9092,vm1:9092,vm2:9092 --topic testnew

  3. pom 文件

    <dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.8_2.11</artifactId>
    <version>1.6.1</version>
    </dependency>
    
  4. 代码实现

    /**
      * flink 从kafka中读取数据  
      */
    object KafkaCousumerToflink {
      def main(args: Array[String]): Unit = {
        //这种可有
        demo01
      }
      def demo01: Unit = {
        val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
        val properties = new Properties()
        properties.setProperty("bootstrap.servers", "vm2:9092")
        // only required for Kafka 0.8
        properties.setProperty("zookeeper.connect", "vm2:2181")
        properties.setProperty("group.id", "test")
        val stream = env
          .addSource(new FlinkKafkaConsumer08[String]("mdj", new SimpleStringSchema(), properties))
          .print()
        env.execute("KafkaCousumerToflink")
      }
    }
    
    

Transform

Transformation 的介绍

Flink 提供了大量的算子操作

一.  Map

输入一个参数产生一个参数,map 的功能是对输入的参数进行转换操作。
val streamMap = stream.map { x => x * 2 }

二.  flatMap

输入一个参数,产生 0、1 或者多个输出,这个多用于拆分操作。
val streamFlatMap = stream.flatMap{
x => x.split(" ")
}

三.  filter

结算每个元素的布尔值,并返回为 true 的元素。
val streamFilter = stream.filter{
x => x == 1
}

四.  KeyBy

DataStream → KeyedStream:输入必须是Tuple类型,逻辑地将一个流拆分成不相交的分区,每个分区包含具有相同key的元素,在内部以hash的形式实现的。
注意:以下类型无法作为key。

  1. POJO类,且没有实现 hashCode 函数
  2. 任意形式的数组类型

五.  Distinct

去重

六.  join 和 outJoin

关联

七.  cross

求笛卡尔积

八.  reduce

滚动合并操作,合并当前元素和上一次合并的元素结果
//求各个渠道的累计个数
val value: DataStream[(Int, Int)] = env.fromElements((1, 2), (1, 3))
val kst: KeyedStream[(Int, Int), Tuple] = value.keyBy(0)
kst.reduce { (t1, t2) => (t1._1, t1._2 + t2._2) }.print().setParallelism(1)
env.execute()

九.  fold

用一个初始的一个值,与其每个元素进行滚动合并操作。
private def myFold(env: StreamExecutionEnvironment): Unit = {
val value: DataStream[(Int, Int)] = env.fromElements((1, 2), (1, 3))
val kst: KeyedStream[(Int, Int), Tuple] = value.keyBy(0)
val ds: DataStream[String] = kst.fold("")((str, i) => {
str + “-” + i
})
ds.print()
env.execute()
}

复杂的方法

  1. aggregation

          KeyedStream --> DataStream:分组流数据的滚动聚合操作: min 和 minBy 的区别是 min 返回的是一个最小值,而 minBy 返回的是其字段中包含的最小值元素(同样原理使用与 max 和 maxBy)。

  2. window

          KeyedStream --> DataStream:windows 是在一个分区的 KeyedStream 中定义的,windows 根据某些特性将每个 key 的数据进行分组(例如:在 5s 内到达的数据)。

  3. windowAll

          DataStream --> AllWindowedStream:Windows 可以在一个常规的 DataStream 中定义,Windows 根据某些特性对所有的流(例如:5s内到达的数据)。这个操作在很多情况下都不是并行操作的,所有的记录都会聚集到一个 windowAll 的操作任务中。

  4. window apply

          WindowedStream --> DataStream,AllWindowedStream --> DataStream:将一个通用的函数作为一个整体传递给window

  5. window reduce

           WindowedStream --> DataStream:给窗口赋予一个reduce的功能,并返回一个reduce的结果。

  6. window fold

           WindowedStream --> DataStream:给窗口赋予一个fold的功能,并返回一个fold后的结果

  7. aggregation on windows

           WindowedStream --> DataStream:对 window 的元素做聚合操作,min 和 minBy 的区别是 min 返回的是最小值,而 minBy 返回的是包含最小值字段的元素。(同样原理适用于 max 和 maxBy )

  8. union

在这里插入图片描述

      DataStream --> DataStream:对两个或两个以上的 DataStream 做 union 操作,产生一个包含所有的 DataStream 元素的新 DataStream 。注意:如果将一个 DataStream 和自己做union操作,在新的 DataStream 中,将看到每个元素重复两次

private def myUnion(env: StreamExecutionEnvironment): Unit = {
  //myConnAndCoMap(env)
  val dsm: DataStream[Int] = env.fromElements(1, 3, 5)
  val dsm01: DataStream[Int] = env.fromElements(2, 4, 6)
  val unit: DataStream[Int] = dsm.union(dsm01)
  unit.print()
  env.execute()
}

  1. window join

          DataStream,DataStream --> DataStream:根据给定的 key 和 window 对两个 DataStream 做 join 操作

  2. window coGroup

          DataStream,DataStream --> DataStream:根据一个给定的 key 和 window 对两个 DataStream 做 CoGroups 操作

  3. connect

在这里插入图片描述

      DataStream,DataStream --> ConnectedStreams:连接两个保持她们类型的数据流。

12. coMap 、coFlatMap

在这里插入图片描述

      ConnectedStreams --> DataStream:作用于 connected 数据流上,功能与 map 和 flatMap 一样

	//合并以后打印
private def myConnAndCoMap(env: StreamExecutionEnvironment): Unit = {
  env.setParallelism(1)
  val src: DataStream[Int] = env.fromElements(1, 3, 5)
  val stringMap: DataStream[String] = src.map(line => "x " + line)
  val result = stringMap.connect(src).map(new CoMapFunction[String, Int, String] {
    override def map2(value: Int): String = {
      "x " + (value + 1)
    }
    override def map1(value: String): String = {
      value
    }
  })
  result.print()
  env.execute()
}
  1. split
    在这里插入图片描述

          DataStream --> SplitStream:根据某些特征把一个 DataStream 拆分成两个或多个 DataStream

  2. select

在这里插入图片描述

      SplitStream --> DataStream:从一个 SplitStream 中获取一个或多个 DataStream

   private def selectAndSplit(env: StreamExecutionEnvironment): Unit = {
  val dsm: DataStream[Long] = env.fromElements(1l, 2l, 3l, 4l)
  val split:SplitStream[Long] = dsm.split(new OutputSelector[Long] {
    override def select(out: Long): lang.Iterable[String] = {
      val list = new util.ArrayList[String]()
      if (out % 2 == 0) {
        list.add("even")
      } else {
        list.add("odd")
      }
      list
    }
  })
  split.select("odd").print().setParallelism(1)
  env.execute()
}

  1. iterate

          DataStream --> IterativeStream --> DataStream:在流程中创建一个反馈循环,将一个操作的输出重定向到之前的操作,这对于定义持续更新模型的算法来说很有意义的。

  2. extract timestamps

          DataStream --> DataStream:提取记录中的时间戳来跟需要事件时间的 window 一起发挥作用.

Sink

Flink + kafka

      Flink 没有类似于 spark 中 foreach 方法,让用户进行迭代的操作。虽有对外的输出操作都要利用 Sink 完成。最后通过类似如下方式完成整个任务最终输出操作。

myDstream.addSink(new MySink(xxxx))

      官方提供了一部分的框架的 sink。除此以外,需要用户自定义实现 sink。

在这里插入图片描述

Kafka Sink

创建 maven 项目,引入 pom 依赖。

<!-- https://mvnrepository.com/artifact/org.apache.flink/flink-connector-kafka-0.11 -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.8_2.11</artifactId>
    <version>1.6.1</version>
</dependency>

代码实现

/**
  * 把数据写入到kafka中
  */
object KafkaProducerFromFlink {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    val dsm: DataStream[String] = env.fromElements("1", "2")
    val prop: Properties = new Properties()
    prop.setProperty("bootstrap.servers", "vm2:9092")
    val value: FlinkKafkaProducer08[String] = new FlinkKafkaProducer08("mdj", new SimpleStringSchema(), prop)
    dsm.addSink(value)
    env.execute()
  }
}

Redis Sink

创建 maven 工程,引入 pom

<!-- https://mvnrepository.com/artifact/org.apache.bahir/flink-connector-redis -->
<dependency>
    <groupId>org.apache.bahir</groupId>
    <artifactId>flink-connector-redis_2.11</artifactId>
    <version>1.0</version>
</dependency>

代码实现

import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.streaming.connectors.redis.RedisSink
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig
import org.apache.flink.streaming.connectors.redis.common.mapper.{RedisCommand, RedisCommandDescription, RedisMapper}
import org.apache.flink.streaming.api.scala._

object MyRedisUtil {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.fromCollection(List(("flink","redis"))).map( x=>(x._1,x._2+"" )).addSink(MyRedisUtil.getRedisSink()).setParallelism(1)
    env.execute("redissink")
  }
  val conf = new FlinkJedisPoolConfig.Builder().setHost("192.168.44.127").setPort(6379).build()

  def getRedisSink(): RedisSink[(String,String)] ={
    new RedisSink[(String,String)](conf,new MyRedisMapper)
  }

  class MyRedisMapper extends RedisMapper[(String,String)]{
    override def getCommandDescription: RedisCommandDescription = {
      //      new RedisCommandDescription(RedisCommand.HSET, "channel_count")
      new RedisCommandDescription(RedisCommand.SET ,"myset" )
    }

    override def getValueFromData(t: (String, String)): String = t._2

    override def getKeyFromData(t: (String, String)): String = t._1
  }
}

Elasticsearch

引入pom

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-elasticsearch6_2.11</artifactId>
    <version>1.7.0</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>

添加MyEsUtil

import java.util
import com.alibaba.fastjson.{JSON, JSONObject}
import org.apache.flink.api.common.functions.RuntimeContext
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.connectors.elasticsearch.{ElasticsearchSinkFunction, RequestIndexer}
import org.apache.flink.streaming.connectors.elasticsearch6.ElasticsearchSink
import org.apache.http.HttpHost
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.client.Requests
import org.apache.flink.api.scala._
/**
  * flink数据下沉到es中
  */

object MyEsUtil {
  def main(args: Array[String]): Unit = {
    val esSink: ElasticsearchSink[String] = MyEsUtil.getElasticSearchSink("gmall0503_startup")
    //获取执行的环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //得到数据
   val ds: DataStream[String] = env.fromCollection(List("key1", "value1"))
    //下沉数据报错(数据格式不正确造成的)
    ds.addSink(esSink)
    env.execute()
  }
  val httpHosts = new util.ArrayList[HttpHost]
  httpHosts.add(new HttpHost("vm0", 9200, "http"))
  httpHosts.add(new HttpHost("vm1", 9200, "http"))
  httpHosts.add(new HttpHost("vm2", 9200, "http"))
  def getElasticSearchSink(indexName: String): ElasticsearchSink[String] = {
    val esFunc = new ElasticsearchSinkFunction[String] {
      override def process(element: String, ctx: RuntimeContext, indexer: RequestIndexer): Unit = {
        println("试图保存:" + element)
        val jsonObj: JSONObject = JSON.parseObject(element)
        val indexRequest: IndexRequest = Requests.indexRequest().index(indexName).`type`("_doc").source(jsonObj)
        indexer.add(indexRequest)
        println("保存1条")
      }
    }
    val sinkBuilder = new ElasticsearchSink.Builder[String](httpHosts, esFunc)
    //刷新前缓冲的最大动作量
    sinkBuilder.setBulkFlushMaxActions(10)
    sinkBuilder.build()
  }
}

JDBC 自定义 sink

引入pom

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

添加MyJdbcSink


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

import com.bw.StreamSink.Stuents
import org.apache.flink.api.common.functions.MapFunction
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.datastream.{DataStream, SingleOutputStreamOperator}
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction
object MyJdbcSink {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
   // val source: DataStream[String] = env.socketTextStream("vm2", 9999)
    val source: DataStream[String] = env.readTextFile("d:/person.txt")
    val map: SingleOutputStreamOperator[Stuents] = source.map(new MapFunction[String, Stuents]() {
      override def map(value: String): Stuents = {
        val split: Array[String] = value.split(",")
        val stu: Stuents = new Stuents
        println(split(0))
        stu.setId(split(0))
        stu.setName(split(1))
        stu.setAge(split(2).toInt)
        stu
      }
    })
    map.addSink(new SinkToMySql())

    env.execute("MyJdbcSink")
  }
  case class student(id: String, name: String,age:String)

  class SinkToMySql() extends RichSinkFunction[Stuents] {

    var conn: Connection = null;
    var ps: PreparedStatement = null

    val driver = "com.mysql.jdbc.Driver"
    val url: String = "jdbc:mysql://vm2:3306/myflink"

    val username = "root"
    val password = "123456"
    val maxActive = "20"

    //初始化的操作
    override def open(parameters: Configuration): Unit = {
      super.open(parameters)
      super.open(parameters)
      Class.forName("com.mysql.jdbc.Driver")
      conn = DriverManager.getConnection(url, username, password)
      conn.setAutoCommit(false)
    }
    //反复调用的函数
    override def invoke(value: Stuents): Unit = {
      val sql: String = "insert into student(name,age) values(?,?)"
      ps = conn.prepareStatement(sql)
      //ps.setString(0,value.getId)
      ps.setString(1,value.getName)
      ps.setString(2,value.getAge.toString)
      ps.execute()
      conn.commit()
    }
    override def close(): Unit = {
      super.close()
      if (conn != null) {
        conn.close()
      }
    }
  }
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flink自定义SinkSource是指用户可以根据自己的需求,编写自己的数据源和数据输出方式。Flink提供了一些内置的SinkSource,但是有时候用户需要根据自己的业务需求,自定义数据源和数据输出方式。 自定义SinkSource需要实现Flink提供的接口,例如实现SinkFunction接口来自定义数据输出方式,实现SourceFunction接口来自定义数据源。用户可以根据自己的需求,实现这些接口,然后将自定义的SinkSource应用到Flink程序中。 自定义SinkSource可以帮助用户更好地满足自己的业务需求,提高数据处理的效率和准确性。 ### 回答2: Flink自定义SinkSource方便开发人员根据特定业务需求定制化的数据输入和输出。这也是Flink作为DataStream处理引擎的一个强大特性。 自定义Sink的主要作用是将Flink处理的数据流输出到外部存储或处理系统中,如Kafka、Hadoop、Elasticsearch、MySQL等。通过自定义Sink,我们可以满足不同业务场景下,数据输出的不同需求。 自定义Sink的实现需要继承Flink提供的`RichSinkFunction`或者`SinkFunction`抽象类,并实现其抽象方法。`RichSinkFunction`中提供了一些状态管理的方法,如`open`、`close`等,我们可以在这些方法中添加额外的代码逻辑。自定义的SinkFunction可以重写invoke方法,将不需要状态管理的代码集中在此方法中。 自定义Source的主要作用是将外部数据源中的数据读取并发送给Flink的DataStream处理模块。自定义Source可以读取各种类型的数据源,如Kafka、文件、Socket等。 自定义Source的实现需要继承Flink提供的`RichParallelSourceFunction`或者`SourceFunction`抽象类,并实现其抽象方法。`RichParallelSourceFunction`中支持在并行算子中运行,因此对于大规模数据的处理尤为适合。 在自定义Source中,需要实现一个`run`方法和一个`cancel`方法。`run`方法中是数据源处理逻辑的主要实现,`cancel`方法用于停止数据源的读取。我们还可以通过Flink提供的Checkpoint机制来管理数据源。 总之,自定义SinkSourceFlink处理数据流的重要特性,使得开发人员可以根据业务需求灵活定制化的输入输出逻辑。 ### 回答3: Flink是一个开源流式处理框架,它提供了丰富的内置SinkSource,同时也支持用户自定义的SinkSource,以便满足不同的业务需求。 自定义Sink可以用于将流式数据写入外部系统中,比如数据库、消息队列和文件系统等。Flink提供了一个简单的接口SinkFunction,通过实现该接口可以快速开发自己的SinkSinkFunction接口定义了一个抽象方法invoke(),该方法是在每个输入元素处理完成时被调用。开发者需要编写自己的业务逻辑,在invoke()中实现将数据写入目标系统的逻辑。 自定义Source可以用于从外部系统读取数据,并将其逐个交付给Flink程序进行处理。同样地,Flink也提供了一个简单的接口SourceFunction,通过实现该接口可以快速开发自己的SourceSourceFunction接口定义了两个抽象方法:run()和cancel()。run()方法是在源自生命周期内调用的,它是源自执行主逻辑的地方。cancel()方法是用于清理资源的。开发者需要在run()方法中编写从外部系统读取数据的逻辑,并且能够异步地产生数据,最后将数据通过SourceContext将数据一条一条源源不断地输出。 自定义SinkSourceFlink框架中非常常用的一个扩展方式,它可以满足用户自定义的需求,在具体的业务场景中,能够灵活的使用自定义SinkSource对数据的处理进行个性化的定制化。同时,自定义SinkSource的开发也相对简单,可以通过实现简单的接口,快速完成自定义SinkSource的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值