一. 在新版本中 flink 默认处理的是事件时间而不是 以前默认的处理时间 在学习的时候安装新版本 步骤容易报错 大概的意思就是设置mark 或者将 事件类型 换成 Processing Time
@deprecated In Flink 1.12 the default stream time characteristic has been changed to
* [[TimeCharacteristic.EventTime]] , thus you don't need to call this method for
* enabling event-time support anymore. Explicitly using processing-time windows and
* timers works in event-time mode. If you need to disable watermarks, please use
* [[org.apache.flink.api.common.ExecutionConfig#setAutoWatermarkInterval(long]]. If
* you are using [[TimeCharacteristic.IngestionTime]], please manually set an
* appropriate [[WatermarkStrategy]]. If you are using generic "time window"
* operations (for example [[KeyedStream.timeWindow()]] that change behaviour based
* on the time characteristic, please use equivalent operations that explicitly
* specify processing time or event time.
In Flink 1.12 the default stream time characteristic has been changed to [[TimeCharacteristic.EventTime]]
二 这里主要是对设置watermark 一些示例代码主要方便 像我一样初学者的一些迷惑
val env = StreamExecutionEnvironment.getExecutionEnvironment
// val value = env.socketTextStream("localhost",7777)
val value = env.addSource(new PerSource)
env.setRestartStrategy(RestartStrategies.noRestart())
//每15秒内 每个传感器的最小值
env.setRestartStrategy(RestartStrategies.noRestart())
//处理的类型 时流还是批处理 还是自动 建议不要去设置
// env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC)
//这里是设置自动生成水印的时间间隔 因为由于大数据数据量很大所以每一个数据都生成一个水印会浪费性能 况且很多时候一批数据中有很多数据时间是一样的 所以这里都是用周期性的生成水印 默认是200毫秒这里也可以根据自身设定
env.getConfig.setAutoWatermarkInterval(200L)
//这里最新的flink 是事件时间为默认项
//这里就是设置所要处理事件的类型 事件时间 处理时间 数据进入时间 如果是处理时间那么就不需要设置太多因为本身就是自身处理的时间但是出现乱序后 会影响 数据最终的准确性
// env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime) //处理时间
val value1 = value
//必须加上 flink
//这里就是分配时间戳和水印了 分配时间戳的意思就是 此次处理数据是按照哪个字段为时间 来进行处理 而 水印就是延迟时间去关闭桶 这里有很多设置一般都是使用过 flink自己封装好的方法 比如BoundedOutOfOrdernessTimestampExtractor
/*.assignTimestampsAndWatermarks(new timestamps.BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(1)) {
override def extractTimestamp(element: SensorReading): Long = {
element.timestamp
}
})*/
// .assignTimestampsAndWatermarks(new waterMarkDeom(Time.seconds(3)))
//如果使用最新的会发现 上面的使用方式 已经被标记了 推荐是用过 WatermarkStrategy 水印的策略 来进行设置 , 这里 flink 也封装了三个方法 为我们所用1)1)1)forBoundedOutOfOrderness 就是平时用的有界输出数据 2).forMonotonousTimestamps()自增长输出 这里自增长大概意思就是事件时间没有乱序都是正确的 不需要去设置水印(有不对的地方欢迎指正 目前也是正在学习) 3) noWatermarks 这个就不用翻译了大家都懂 然后需要在后面添加事件时间的取自哪个字段也是 flink自己封装的方法.withTimestampAssigner 时间戳分配器 其中有两个重写的方法 一个是添加 SerializableTimestampAssigner 另一个 TimestampAssignerSupplier 这里我只用了第一个第二个没有研究 等有懂的大佬 来评论告诉下 然后 最后还一个.withIdleness(Duration.ofMillis(1))) 这个方法可添加也可以不添加 就不会影响启动了 翻译的大概意思是就是flink 为分布式 处理流式框架 在由上游数据发送下游的时候需要等到所有分区的数据到达后 以最后的数据到达时间为时间但是有可能出现超时会影响下一步操作所以这个配置了之后大概的意思是保留空闲分区的时间之后将不会影响向下游广播 数据操作流程
.assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofMillis(1 )).withTimestampAssigner(new SerializableTimestampAssigner[SensorReading]() {
override def extractTimestamp(element: SensorReading, recordTimestamp: Long): Long = {
element.timestamp*1000L
}
}).withIdleness(Duration.ofMillis(1)))
/*.assignTimestampsAndWatermarks(WatermarkStrategy.forMonotonousTimestamps().withTimestampAssigner(new SerializableTimestampAssigner[SensorReading]() {
override def extractTimestamp(element: SensorReading, recordTimestamp: Long): Long = {
element.timestamp*1000L
}
}))*/
/*.assignTimestampsAndWatermarks(WatermarkStrategy.forGenerator(new waterMarkStrategyDemo).withTimestampAssigner(new SerializableTimestampAssigner[SensorReading] {
override def extractTimestamp(element: SensorReading, recordTimestamp: Long): Long = {
element.timestamp
}
}).withIdleness(Duration.ofSeconds(1)))*/
//WatermarkStrategy.forGenerator(new waterMarkStrategyDemo)
/* .assignTimestampsAndWatermarks(WatermarkStrategy.forGenerator(new waterMarkStrategyDemo)withTimestampAssigner(new SerializableTimestampAssigner[SensorReading] {
override def extractTimestamp(element: SensorReading, recordTimestamp: Long): Long = {
element.timestamp
}
}))*/
/* .assignTimestampsAndWatermarks(WatermarkStrategy.forMonotonousTimestamps().withTimestampAssigner((new SerializableTimestampAssigner[SensorReading]() {
override def extractTimestamp(element: SensorReading, recordTimestamp: Long): Long = {
element.timestamp
}
})))*/
//排好序的升序的 数据 来的时间 不用定义watermark 数据没有乱序
//.assignAscendingTimestamps(x=>x.timestamp*1000)
.keyBy(_.id)
//.timeWindow(Time.seconds(12)).reduce((x, y) => x)
//滚动窗口 头尾相接、
.timeWindow(Time.seconds(1))
//这个设置时允许时间窗口处理过后 保留窗口的时间 大概的意思比如 0-1秒的窗口操作了但是保留1毫秒 然后在这1毫秒内 如果还有0-1秒的数据到来也会参与到数据的操作
.allowedLateness(Time.milliseconds(1))
//.aggregate(AggregationType.MAX,2)
//输出哪个数据延迟到来 这就是将保留窗口 都已经关闭了 0-1秒的数据 来了 就会进入到这里测流输出 在之后可用value1.getSideOutput(new OutputTag[SensorReading]("later")) 接住 然后 可以在处理比如 发送到一个 延迟数据 topic 然后 跟以前的业务数据 操作 来避免数据丢失 比如最小值 那么跟 最新的数据取比对 如果 比最新的数据要小 那么就更新如果不是 就可以抛弃了
.sideOutputLateData(new OutputTag[SensorReading]("later"))
//.window(TumblingProcessingTimeWindows.of(Time.seconds(1)))//滑动时间窗口
// .window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.reduce((x, y) => x)
//.window(EventTimeSessionWindows.withGap(Time.seconds(1)))//会话时间窗口 1s进来的都算一个桶
// .timeWindow()准备弃除
// .countWindow(10)
// .trigger(new sw)
// .evictor(new sa)
// .allowedLateness(Time.seconds(1))
// .sideOutputLateData(new OutputTag("ds"))
// unit.getSideOutput(new OutputTag("ds"))
value1.print("ok")
val value2: DataStream[SensorReading] = value1.getSideOutput(new OutputTag[SensorReading]("later"))
env.execute("ss ss")
四 flinksql 创建事件时间
flinksql 添加水印 wartermark
- 第一如果是时间戳类型可以直接使用TIMESTAMP 类型
- 给的是日期 可以用 varchar() 类型然后用 TO_TIMESTAMP() 类型去转换
"""|CREATE TABLE file (
|id varchar(20) not null,
|ts bigint,
|pt varchar(20), --如果是时间戳 可以用 TIMESTAMP 类型
|KS AS TO_TIMESTAMP(pt), --如果使用 TIMESTAMP 类型 这里可以省略 直接吧 pt当成事件时间 WATERMARK FOR pt AS pt - INTERVAL '5' SECOND
|WATERMARK FOR KS AS KS - INTERVAL '5' SECOND
|)
| with
|(
| 'connector' = 'filesystem',
| 'path' = 'C:\\Users\\zhou\\IdeaProjects\\flinktable\\conf\\input.txt',
| 'format' = 'csv'
|)
|
|""".stripMargin
flink Table 流处理添加 事件时间 一定要添加水印 指定谁是事件时间
val value: DataStream[String] = env.readTextFile("C:\\Users\\zhou\\IdeaProjects\\flinktable\\conf\\input.txt")
val unit = value.map(x => {
val strings = x.split(",")
Demo(strings(0), strings(1).toInt, strings(2).toLong)
}
//打水印 时间时间
).assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofMillis(1)).withTimestampAssigner(new SerializableTimestampAssigner[Demo]() {
override def extractTimestamp(element: Demo, recordTimestamp: Long): Long = {
element.timeStr
}
//指定两个方式的时间时间 timeStr 我的 时间戳本身就是毫秒的所以不用乘 1000L
//bsTableEnv.fromDataStream(unit,'id,'age,'timeStr,'pt.proctime())
val table: Table = bsTableEnv.fromDataStream(unit, 'id, 'age, 'timeStr.rowtime)
五 Group Windows (分组窗口)
- Group Windows 使用 windows (w:GroupWindow)子句定义的,并且必须由 as 子句指定一个别名
- 为了按窗口对表分组,窗口的别名必须在group by 子句中 像常规的分组字段一段引用
// table api
input.window([w:GroupWindow] as 'w') // 定于窗口 例如 滑动 或者滚动 别名 w
.groupBy(`w,`a) // 按照字段a 和窗口 w 分组
.select('a,'b.sum) //聚合
//滚动窗口要用Tumble 类定义
//
.window(Tumble over 10.minutes on `rowtime as ` w) //rowtime proctime 是定义好的事件时间或者处理时间的字段名称替换
// 处理时间
.window(Tumble over 10.minutes on `proctime as ` w)
// Row-count Window 滚动是的计数窗口 处理时间
.window(Tumble over 10.rows on `proctime as ` w)
// 滑动窗口 Slide
.window(Slide over 10.minutes every 5.minutes on `rowtime as ` w)
}
- Table Api 提供了一组具有特定语句的预定义的window 类 这些类会被转化为底层 DataStream 或者 DataSet 的窗口操作
//sql
// Group Windows 定义在 SQL中查询Group BY 子句中
TUMBLE( time_attr,interval) --第一个蚕食是时间字段 第二个参数时间是长度 (滚动)
HOP( time_attr,interval,interval) --第一个蚕食是时间字段 第二个参数滑动步长 第三个参数是窗口长度 (滑动)
import java.time.Duration
import akka.stream.actor.WatermarkRequestStrategy
import com.ibm.icu.impl.locale.LocaleDistance.Data
import org.apache.flink.api.common.eventtime._
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.table.api._
import org.apache.flink.table.api.bridge.scala._
import org.apache.flink.table.descriptors.{Csv, FileSystem, Rowtime, Schema}
import org.apache.flink.types.Row
import ru.yandex.clickhouse.ClickHouseArray
object flinkSql {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
// env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
val bsTableEnv = StreamTableEnvironment.create(env, bsSettings)
/*
01,23,1621412815000
03,21,1621412820000
01,32,1621412825000
02,42,1621412835000
02,23,1621412845000
01,42,1621412855000
03,24,1621412915000
02,21,1621412825000
03,53,1621412835000
*/
val value: DataStream[String] = env.readTextFile("C:\\Users\\zhou\\IdeaProjects\\flinktable\\conf\\input.txt")
val unit = value.map(x => {
val strings = x.split(",")
Demo(strings(0), strings(1).toInt, strings(2).toLong)
}
//打水印 时间时间
).assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofMillis(1)).withTimestampAssigner(new SerializableTimestampAssigner[Demo]() {
override def extractTimestamp(element: Demo, recordTimestamp: Long): Long = {
element.timeStr
}
}))
//table api
val table: Table = bsTableEnv.fromDataStream(unit, 'id, 'age, 'timeStr.rowtime)
val table1 = table.window(Tumble over 5.minutes() on 'timeStr as 'w).groupBy('w,'id).select('id,'age.sum() as 'was)
table1.toAppendStream[Row].print("s")
env.execute("ssss")
/*
input
01,23,2021-05-05 00:01:00
03,21,2021-05-05 00:01:05
01,32,2021-05-05 00:01:07
02,42,2021-05-05 00:01:10
02,23,2021-05-05 00:01:12
01,42,2021-05-05 00:01:15
03,24,2021-05-05 00:01:20
02,21,2021-05-05 00:01:21
03,53,2021-05-05 00:01:22
*/
//sql
bsTableEnv.executeSql(
"""
|CREATE TABLE file (
|id varchar(20) not null,
|ts bigint,
|pt varchar(20), --如果是时间戳 可以用 TIMESTAMP 类型
|KS AS TO_TIMESTAMP(pt),
|WATERMARK FOR KS AS KS - INTERVAL '5' SECOND
|)
| with
|(
| 'connector' = 'filesystem',
| 'path' = 'C:\\Users\\zhou\\IdeaProjects\\flinktable\\conf\\input.txt',
| 'format' = 'csv'
|)
|
|""".stripMargin)
bsTableEnv.executeSql(
"""
|CREATE TABLE fileout (
|id varchar(20) not null,
|ts bigint
|)
| with
|(
| 'connector' = 'print'
|)
|
|""".stripMargin)
bsTableEnv.executeSql("insert into fileout select id,sum(ts) from file group by id ,tumble(KS,interval '30' second) ")
}
}
case class Demo(id: String, age: Int, timeStr: Long)
五 Over Windows (分组窗口)
-
Over Window 聚合是标准SQL中已有的(over子句),可以在查询的SELECT 子句中定义
-
Over window 聚合 会针对每个输入行 机选相邻范围内的聚合
-
Over windows 使用window(w:overwindow*)子句定义 并在select () 方法中通过别名来引用
// table api
input.window([w:OverWindow] as 'w')
.select('a,'b.sum over 'w)
-
Table API 提供了Over类,来配置Over窗口的属性
-
无界Over Window
- 可以在事件时间或处理时间以及指定为为时间间隔或行计数范围内,定义Over Windows
- 无界 over window 是使用常量指定的
// 无界的事件时间 over window
.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_RANGE as ’ w )
//无界的处理时间 over window
.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_RANGE as ’ w )
//无界的事件时间 Row-count over window
.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_ROW as ’ w )
//无界的处理时间 Row-count over window
.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_ROW as ’ w )
import java.time.Duration
import akka.stream.actor.WatermarkRequestStrategy
import com.ibm.icu.impl.locale.LocaleDistance.Data
import org.apache.flink.api.common.eventtime._
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.table.api._
import org.apache.flink.table.api.bridge.scala._
import org.apache.flink.table.descriptors.{Csv, FileSystem, Rowtime, Schema}
import org.apache.flink.types.Row
import ru.yandex.clickhouse.ClickHouseArray
object flinkSql {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
// env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
val bsTableEnv = StreamTableEnvironment.create(env, bsSettings)
val value: DataStream[String] = env.readTextFile("C:\\Users\\zhou\\IdeaProjects\\flinktable\\conf\\inputONE.txt")
val unit = value.map(x => {
val strings = x.split(",")
Demo(strings(0), strings(1).toInt, strings(2).toLong)
}
//打水印 时间时间
).assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofMillis(1)).withTimestampAssigner(new SerializableTimestampAssigner[Demo]() {
override def extractTimestamp(element: Demo, recordTimestamp: Long): Long = {
element.timeStr
}
}))
val table: Table = bsTableEnv.fromDataStream(unit, 'id, 'age, 'timeStr.rowtime)
//Over Window---------------------
//SQL
bsTableEnv.executeSql(
"""
|CREATE TABLE file (
|id varchar(20) not null,
|ts bigint,
|pt varchar(20), --如果是时间戳 可以用 TIMESTAMP 类型
|KS AS TO_TIMESTAMP(pt),
|WATERMARK FOR KS AS KS - INTERVAL '5' SECOND
|)
| with
|(
| 'connector' = 'filesystem',
| 'path' = 'C:\Users\zhou\IdeaProjects\flinktable\conf\input.txt',
| 'format' = 'csv'
|)
|
|""".stripMargin)
bsTableEnv.executeSql(
"""
|CREATE TABLE fileout (
|id varchar(20) not null,
|ts bigint
|)
| with
|(
| 'connector' = 'print'
|)
|
|""".stripMargin)
bsTableEnv.executeSql(
"""insert into fileout
|select id,sum(ts) over ow from file
|window ow as
|( partition by id order by KS rows between 2 preceding and current row )""".stripMargin)
// Table API -------------------------------
/* val table1 = table.window(Over partitionBy 'id orderBy 'timeStr preceding UNBOUNDED_RANGE as 'w).select('id,'age.sum() over 'w ,'timeStr)
table1.toAppendStream[Row].print("s")
env.execute("over ")*/
}
}
case class Demo(id: String, age: Int, timeStr: Long)