文章目录

  • 1.flink与spark的对比
  • 2.flink内部原理
  • 2.1内部组件
  • 2.1.1environment
  • 2.1.2source
  • 2.1.3transform
  • 2.1.4sink
  • 2.2运行机制
  • 2.2.1任务提交模式(yarn)
  • 2.3worker和slots
  • 2.4EventTime和window
  • 2.4.1Eventtime
  • 2.4.2window
  • 2.4.1
  • 3.flink的API操作
  • 3.1environment搭建
  • 3.2Kafkasource
  • 3.3split-select
  • 3.4合并
  • 3.5Sink
  • 3.5.1kafkasink
  • 3.5.2redis
  • 3.5.3自定义JDBC


1.flink与spark的对比

  • spark是批处理,处理有界、持久化、大量需要访问整个数据集才能完成计算,适合做离线计算
  • fink是流处理,处理无界、实时,不需要访问整个数据集就能完成计算,适合做实时计算
  • spark的实时处理,只是缩短了批处理的时间,但也可以完成实时计算
  • flink的实时处理,是真正的流式计算,延迟性要低于spark
  • flink是事件驱动型,同样的框架还有kfka,而且Kafka通过两段式提交可以真正的完成exactly-once(对于发送的massage,receiver确保只收到一次)
  • flink同时也有缺憾,他对sql语句的支持没有spark那么好用,因此许多大厂会在flink上开发,完成自己的需求

2.flink内部原理

2.1内部组件

2.1.1environment

  • flink的环境主要使用getExecutionEnvironment,再本地使用就是本地模式,集群使用就是集群模式
  • 还有两个createLocalEnvironment(本地)、createRemoteEnvironment(远程)

2.1.2source

  • 主要来源于kafka详见API操作

2.1.3transform

  • flink的transform操作中大部分的算子和spark相似,但也有不同的
  • flink将spark中的reducebykey分成了两个部分,keyby 和reduce
  • flink有一个split-select,类似与flume的拦截器和分区器,将进来的数据打上标签,然后通过select方法获得
  • Union和Connect-CoMap:Union是强硬的让两个数据流在一起,但是他们的数据类型必须要相同,而Connect-CoMap可以两个数据流不同,connect之后进入一个缓冲区培养感情(ConnectedStreams),然后CoMap,作用于ConnectedStreams上,功能与map和flatMap一样,对ConnectedStreams中的每一个Stream分别进行map和flatMap处理。

2.1.4sink

  • flink的sink要求严格,需要继承官方指定的sink

2.2运行机制

2.2.1任务提交模式(yarn)

cdp flink 资源管理_fink基础

  • Flink任务提交后,Client向HDFS上传Flink的Jar包和配置
  • 向Yarn ResourceManager提交任务,ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster
  • ApplicationMaster启动后加载Flink的Jar包和配置构建环境,然后启动JobManager,之后ApplicationMaster向ResourceManager申请资源启动TaskManager
  • ResourceManager分配Container资源后,由ApplicationMaster通知资源所在节点的NodeManager启动TaskManager
  • NodeManager加载Flink的Jar包和配置构建环境并启动TaskManager,TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务。

2.3worker和slots

-每一个worker(taskmannager)上可以有多个slots,提高了并发度

2.4EventTime和window

2.4.1Eventtime

  • 在Flink的流式处理中,绝大部分的业务都会使用eventTime,一般只在eventTime无法使用时,才会被迫使用ProcessingTime或者IngestionTime。
  • Watermark:用于处理乱序事件的,而正确的处理乱序事件,通常用Watermark机制结合window来实现。
    数据流中的Watermark用于表示timestamp小于Watermark的数据,都已经到达了,因此,window的执行也是由Watermark触发的。
    Watermark可以理解成一个延迟触发机制,我们可以设置Watermark的延时时长t,每次系统会校验已经到达的数据中最大的maxEventTime,然后认定eventTime小于maxEventTime - t的所有数据都已经到达,如果有窗口的停止时间等于maxEventTime – t,那么这个窗口被触发执行。

2.4.2window

  1. 滚动窗口(Tumbling Windows):将数据依据固定的窗口长度对数据进行切片。
    特点:时间对齐,窗口长度固定,没有重叠。
    适用场景:适合做BI统计等(做每个时间段的聚合计算)。
  2. 滑动窗口(Sliding Windows):滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口长度和滑动间隔组成。
    特点:时间对齐,窗口长度固定,有重叠。
    适用场景:对最近一个时间段内的统计(求某接口最近5min的失败率来决定是否要报警
  3. 会话窗口(Session Windows):
    由一系列事件组合一个指定时间长度的timeout间隙组成,类似于web应用的session,也就是一段时间没有接收到新数据就会生成新的窗口。
    特点:时间无对齐。

2.4.1

3.flink的API操作

3.1environment搭建

//引入配置
<dependencies>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-scala_2.11</artifactId>
            <version>1.7.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-streaming-scala -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-scala_2.11</artifactId>
            <version>1.7.0</version>
        </dependency>
    </dependencies>

<build>
    <plugins>
    <!-- 该插件用于将Scala代码编译成class文件 -->
    <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.4.6</version>
        <executions>
            <execution>
                <!-- 声明绑定到maven的compile阶段 -->
                <goals>
                    <goal>compile</goal>
                    <goal>testCompile</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

</project>
//最常用
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//local,指定并行度
val env = StreamExecutionEnvironment.createLocalEnvironment(1)
//远程,返回集群执行环境,将Jar提交到远程服务器。需要在调用时指定JobManager的IP和端口号,并指定要在集群中运行的Jar包。
val env = ExecutionEnvironment.createRemoteEnvironment("jobmanager-hostname", 6123,"C://jar//flink//wordcount.jar")

3.2Kafkasource

//Kafka工具类
object MyKafkaUtil {

  val prop = new Properties()

  prop.setProperty("bootstrap.servers","bigdata1:9092")
  prop.setProperty("group.id","test")

  def getConsumer(topic:String ):FlinkKafkaConsumer011[String]= {
      val myKafkaConsumer:FlinkKafkaConsumer011[String] = new FlinkKafkaConsumer011[String](topic, new SimpleStringSchema(), prop)
     myKafkaConsumer
  }
}
//主类
def main(args: Array[String]): Unit = {
          val environment: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

3.3split-select

// 将appstore与其他渠道拆分拆分出来  成为两个独立的流
val splitStream: SplitStream[StartUpLog] = startUplogDstream.split { startUplog =>
  var flags:List[String] =  null
  if ("标签1" == startUplog.ch) {
    flags = List(startUplog.ch)
  } else {
    flags = List("标签2" )
  }
  flags
}
val appStoreStream: DataStream[StartUpLog] = splitStream.select("标签1")
appStoreStream.print("标签1:").setParallelism(1)
val otherStream: DataStream[StartUpLog] = splitStream.select("标签2")
otherStream.print("标签2:").setParallelism(1)

3.4合并

//connect-CoMap
//合并以后打印
val connStream: ConnectedStreams[StartUpLog, StartUpLog] = appStoreStream.connect(otherStream)
val allStream: DataStream[String] = connStream.map(
  (log1: StartUpLog) => log1.ch,
  (log2: StartUpLog) => log2.ch
)
allStream.print("connect::")
//Union
//合并以后打印
val unionStream: DataStream[StartUpLog] = appStoreStream.union(otherStream)
unionStream.print("union:::")

3.5Sink

3.5.1kafkasink

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

//util
def getProducer(topic:String): FlinkKafkaProducer011[String] ={
  new FlinkKafkaProducer011[String](brokerList,topic,new SimpleStringSchema())

//主函数
val myKafkaProducer: FlinkKafkaProducer011[String] = MyKafkaUtil.getProducer("channel_sum")

sumDstream.map( chCount=>chCount._1+":"+chCount._2 ).addSink(myKafkaProducer)

3.5.2redis

//配置
<!-- 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>

//utils
object MyRedisUtil {

 

  val conf = new FlinkJedisPoolConfig.Builder().setHost("hadoop1").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  )
    }

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

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

}
//main
sumDstream.map( chCount=>(chCount._1,chCount._2+"" )).addSink(MyRedisUtil.getRedisSink())

3.5.3自定义JDBC

//配置
<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>

//utils
class MyJdbcSink(sql:String ) extends  RichSinkFunction[Array[Any]] {

  val driver="com.mysql.jdbc.Driver"

  val url="jdbc:mysql://hadoop2:3306/gmall2019?useSSL=false"

  val username="root"

  val password="123123"

  val maxActive="20"

  var connection:Connection=null;

  //创建连接
  override def open(parameters: Configuration): Unit = {
    val properties = new Properties()
    properties.put("driverClassName",driver)
    properties.put("url",url)
    properties.put("username",username)
    properties.put("password",password)
    properties.put("maxActive",maxActive)


    val dataSource: DataSource = DruidDataSourceFactory.createDataSource(properties)
    connection = dataSource.getConnection()
  }

//反复调用
  override def invoke(values: Array[Any]): Unit = {
    val ps: PreparedStatement = connection.prepareStatement(sql )
    println(values.mkString(","))
    for (i <- 0 until values.length) {
      ps.setObject(i + 1, values(i))
    }
    ps.executeUpdate()
  }
  override def close(): Unit = {
    if(connection!=null){
      connection.close()
    }
  }
}
//main
val startUplogDstream: DataStream[StartUpLog] = dstream.map{ JSON.parseObject(_,classOf[StartUpLog])}
val jdbcSink = new MyJdbcSink("insert into z_startup values(?,?,?,?,?)")
startUplogDstream.map(startuplog=>Array(startuplog.mid,startuplog.uid,startuplog.ch,startuplog.area,  startuplog.ts)).addSink(jdbcSink)