import org.apache.flink.api.common.eventtime.{WatermarkStrategy, TimestampAssigner}
import org.apache.flink.api.common.typeinfo.TypeInformation
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.streaming.api.environment.StreamExecutionEnvironment
import org.apache.flink.connector.jdbc.JdbcSink
import org.apache.flink.connector.jdbc.JdbcConnectionOptions
import java.sql.PreparedStatement
import java.time.Instant
import java.util.UUID
import scala.util.Random
// 订单样例类
case class Order(orderId: String, userId: Int, money: Long, timestamp: Long)
// 聚合结果类(用于写入MySQL)
case class UserOrderCount(userId: Int, orderCount: Int, windowStart: Long, windowEnd: Long)
object WindowCountToMySQL {
def main(args: Array[String]): Unit = {
// 创建执行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
// 启用事件时间
env.getConfig.setAutoWatermarkInterval(200) // 每200ms插入Watermark
// 数据源:每2秒生成一条订单
val orderDS = env.addSource(new org.apache.flink.streaming.api.functions.source.RichParallelSourceFunction[Order] {
override def run(ctx: org.apache.flink.streaming.api.functions.source.SourceFunction.SourceContext[Order]): Unit = {
while (true) {
val order = Order(
orderId = UUID.randomUUID().toString,
userId = Random.nextInt(3), // 用户ID: 0,1,2
money = Random.nextInt(101), // 金额0~100
timestamp = System.currentTimeMillis() - 10000 // 模拟略微延迟
)
ctx.collect(order)
Thread.sleep(2000)
}
}
override def cancel(): Unit = {}
})
// 分配时间戳和Watermark(解决网络延迟)
val withTimestampsAndWatermarks = orderDS
.assignTimestampsAndWatermarks(
WatermarkStrategy.forMonotonousTimestamps[Order]()
.withTimestampAssigner(new TimestampAssigner[Order] {
override def extractTimestamp(element: Order, recordTimestamp: Long): Long = element.timestamp
})
)
// 按用户分组,10秒滚动窗口统计订单数
val resultStream = withTimestampsAndWatermarks
.keyBy(_.userId)
.window(TumblingEventTimeWindows.of(Time.seconds(10)))
.apply { (userId: Int, window: org.apache.flink.streaming.api.windowing.windows.TimeWindow, orders: Iterable[Order]) =>
UserOrderCount(
userId = userId,
orderCount = orders.size,
windowStart = window.getStart,
windowEnd = window.getEnd
)
}
// 写入 MySQL 的 sink
resultStream.addSink(
JdbcSink.sink[
UserOrderCount
](
"INSERT INTO user_order_count (user_id, order_count, window_start, window_end) VALUES (?, ?, ?, ?)",
(elem: UserOrderCount, stmt: PreparedStatement) => {
stmt.setInt(1, elem.userId)
stmt.setInt(2, elem.orderCount)
stmt.setLong(3, elem.windowStart)
stmt.setLong(4, elem.windowEnd)
},
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://192.168.72.141:3306/demo?useSSL=false&serverTimezone=UTC")
.withUsername("root")
.withPassword("12345678")
.withDriverName("com.mysql.cj.jdbc.Driver")
.build()
)
)
// 执行任务
env.execute("Flink Window Count to MySQL")
}
}