〇、概述

1、实现内容

使用Scala编写代码,通过Flink的Source、Sink以及时间语义实现实时销量展示

2、过程

(1)导包并下载依赖

(2)创建数据源数据表并写⼊数据

(3)在Mysql数据库中创建统计结果表

(4)编写Flink计算代码

a.参考ShopMysqlSource.scala 代码,进⾏Flink Source 编写,从Mysql读取数据

b.参考GaotuShopFlinkStat.scala代码,进⾏统计逻辑的编写,进⾏FlinkSQL 查询

c.参考ShopStatMysqlSink.scala 代码,进⾏FlinkSink 编写,存⼊数据到Mysql

一、导包并下载依赖

二、创建数据源数据表并写⼊数据

参考执⾏GenerateOrders.scala 代码

package com.gaotu.flink
import org.apache.commons.lang.time.FastDateFormat import java.sql.DriverManager
import java.sql.Connection
import java.util.concurrent.TimeUnit
import scala.util.Random /**
* 向Mysql数据库中添加数据
* CREATE TABLE `g_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '交易id',
`stock_name` varchar(20)DEFAULT NULL COMMENT '商品名称',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`deal_status` varchar(20) DEFAULT NULL COMMENT '交易状态',
`stock_cnt` int(11) DEFAULT NULL COMMENT '商品数量',
`deal_amount` double DEFAULT NULL COMMENT '订单金额',
`oper_time` bigint COMMENT '处理时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=120 DEFAULT CHARSET=utf8;
*
*/
object GenerateOrders { def main(args: Array[String]): Unit = {
val driver = "com.mysql.cj.jdbc.Driver"
val url = "jdbc:mysql://localhost/plato"
val username = "root"
val password = "root"
var connection:Connection = null //商品数组
val stocks = Array("HUAWEI Mate40","Apple iphone13","Apple MacBook Pro 14"
,"ThinkBook 14p","RedmiBook Pro14","飞鹤星飞帆幼儿奶粉","爱他美 幼儿奶粉"
,"李宁运动男卫裤","小米踏步机椭圆机","欧莱雅面膜","御泥坊面膜","欧莱雅男士套装","金六福白酒"
,"牛栏山42度","茅台飞天")
val amount = Array(6569.00,6099.00,14999.00,6799.00,4899.00,275,392,199,1299.00,599,399,389,469,175,1399.00) try {
Class.forName(driver)
connection = DriverManager.getConnection(url, username, password)
val ps = connection.createStatement()
for (i <- 1 to 10000){
val formater = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")
val item = scala.util.Random.nextInt(14)
val deal_time = formater.format(System.currentTimeMillis())
val stock_name = stocks(item)
val user_id = Random.nextInt(100)
val deal_status = "有效"
val stock_cnt = Random.nextInt(20)
val deal_amount = amount(item)
val oper_time = System.currentTimeMillis()
val query = s"insert into g_orders(deal_time,stock_name,user_id,deal_status,stock_cnt,deal_amount,oper_time) values('$deal_time', '$stock_name', $user_id, '$deal_status', $stock_cnt, $deal_amount, $oper_time)"
ps.addBatch(query)
println(query) TimeUnit.SECONDS.sleep(1)
}
ps.executeBatch()
} catch {
case e => e.printStackTrace
}
connection.close()
}
}

三、在Mysql数据库中创建统计结果表

-- 统计结果表 
CREATE TABLE `g_orders_stat` (
`stock_name` varchar(20)DEFAULT NULL COMMENT '商品名称',
`order_cnt` int(11) DEFAULT NULL COMMENT '商品数量',
`stock_sales_cnt` int(11) DEFAULT NULL COMMENT '商品数量',
`stock_sales_amt` double DEFAULT NULL COMMENT '商品数量'
) ENGINE=InnoDB AUTO_INCREMENT=120 DEFAULT CHARSET=utf8;

四、编写Flink计算代码

1、参考ShopMysqlSource.scala 代码,进⾏Flink Source 编写,从Mysql读取数据

package com.gaotu.flink

import org.apache.flink.streaming.api.functions.source.RichSourceFunction
import org.apache.flink.streaming.api.functions.source.SourceFunction.SourceContext
import java.sql.{Connection, DriverManager, PreparedStatement}
import java.util.concurrent.TimeUnit /*
* Mysql Source
*/
class ShopMysqlSource extends RichSourceFunction[(Int, String, Int, Double, Long)] {
val driver = "com.mysql.cj.jdbc.Driver"
val url = "jdbc:mysql://localhost/plato"
val username = "root"
val password = "root" override def run(ctx: SourceContext[(Int, String, Int, Double, Long)]): Unit = {
// 1. 加载MYSQL驱动
Class.forName(driver)
// 2. 建立MYSQL链接
var connection: Connection = DriverManager.getConnection(url, username, password)
var max_id = 0
// 3. 创建PreparedStatement
val sql = s"select id,stock_name,stock_cnt,deal_amount,oper_time from plato.g_orders where id > ${max_id} ;"
// 4. 执行Sql查询
val ps: PreparedStatement = connection.prepareStatement(sql)
for(i <- 0 until 500) {
val queryRequest = ps.executeQuery()
// 5. 遍历结果
while (queryRequest.next()) {
val id = queryRequest.getInt("id")
val stock_name = queryRequest.getString("stock_name")
val stock_cnt = queryRequest.getInt("stock_cnt")
val deal_amount = queryRequest.getDouble("deal_amount")
val deal_time = queryRequest.getLong("oper_time")
ctx.collect((id, stock_name, stock_cnt, deal_amount, deal_time)) if(max_id < id){
max_id = id
}
}
TimeUnit.SECONDS.sleep(1)
}
}
override def cancel(): Unit = {}
}

2、参考GaotuShopFlinkStat.scala代码,进⾏统计逻辑的编写,进⾏FlinkSQL 查询

package com.gaotu.flink

import org.apache.flink.api.scala._
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.watermark.Watermark
import org.apache.flink.table.api.{Table, TableEnvironment}
import org.apache.flink.table.api.scala._ /**
* 实时统计逻辑
*/
object GaotuShopFlinkStat { def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment // 1.创建流处理环境
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) // 2.设置处理时间为事件
val tableEnv = TableEnvironment.getTableEnvironment(env) // 3.获取Table运行环境
val orderDataStream = env.addSource(new ShopMysqlSource) // 4.获取自定义数据源
// 5.添加水印,允许延迟2秒
val watermarkDataStream:DataStream[(Int, String, Int, Double, Long)] = orderDataStream.assignTimestampsAndWatermarks(
new AssignerWithPeriodicWatermarks[(Int, String, Int, Double, Long)]{
var currentTimestamp: Long = _
val delayTime = 2000 // 允许延迟2秒
override def getCurrentWatermark: Watermark = { // 生成一个水印时间
val watermark = new Watermark(currentTimestamp - delayTime) // 让时间窗口延迟2秒计算
watermark
}
// 从订单中获取对应的时间戳
override def extractTimestamp(element: (Int, String, Int, Double, Long), previousElementTimestamp: Long): Long = {
val oper_time = element._5 // 获取订单的事件时间(下单时的时间)
currentTimestamp = Math.max(currentTimestamp, oper_time) // 当前时间与事件时间对比,获取最大值
currentTimestamp
}
} ) // 6.注册LinkSQL数据表
tableEnv.registerDataStream("g_order", watermarkDataStream, 'id,'stock_name,'stock_cnt,'deal_amount,'oper_time,'createTime.rowtime)
// 7.编写SQL语句进行统计
val sql =
"""
| select
| stock_name
| ,count(id) as order_cnt
| ,sum(stock_cnt) as stock_sales_cnt
| ,sum(deal_amount) as stock_sales_amt
| from g_order
| group by
| tumble(createTime, interval '5' second ),
| stock_name
|""".stripMargin
// 8.执行sql语句
val table: Table = tableEnv.sqlQuery(sql)
table.printSchema() // 9.将SQL的执行结果写入到Mysql中
tableEnv.toAppendStream[(String, Long, Int, Double)](table).addSink(new ShopStatMysqlSink)
// 10.执行任务
env.execute()
}
}

3、参考ShopStatMysqlSink.scala 代码,进⾏FlinkSink 编写,存⼊数据到Mysql

package com.gaotu.flink

import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction
import java.sql.{Connection, DriverManager, PreparedStatement} /**
* Mysql Sink
*
* CREATE TABLE `g_orders_stat` (
`stock_name` varchar(20)DEFAULT NULL COMMENT '商品名称',
`order_cnt` int(11) DEFAULT NULL COMMENT '商品数量',
`stock_sales_cnt` int(11) DEFAULT NULL COMMENT '商品数量',
`stock_sales_amt` double DEFAULT NULL COMMENT '商品数量'
) ENGINE=InnoDB AUTO_INCREMENT=120 DEFAULT CHARSET=utf8;
*
*/
class ShopStatMysqlSink extends RichSinkFunction[(String, Long, Int, Double)] { val driver = "com.mysql.cj.jdbc.Driver"
val url = "jdbc:mysql://localhost/plato"
val username = "root"
val password = "root"
var connection: Connection = null
var ps: PreparedStatement = null override def open(parameters: Configuration): Unit = {
Class.forName(driver) // 1. 加载驱动
connection = DriverManager.getConnection(url, username, password) // 2. 创建连接
val sql = "insert into g_orders_stat (stock_name,order_cnt,stock_sales_cnt,stock_sales_amt) values (?,?,?,?);" // 3. 获得执行语句
ps = connection.prepareStatement(sql)
}
override def invoke(value: (String, Long, Int, Double)): Unit = {// 4. 插入数据
try {
ps.setString(1, value._1)
ps.setLong(2, value._2)
ps.setInt(3, value._3)
ps.setDouble(4, value._4)
ps.execute()
} catch {
case e: Exception => println(e.getMessage)
}
}
// 5. 关闭连接操作
override def close(): Unit = {
if(connection != null) {
connection.close()
}
if(ps != null) {
ps.close()
}
}
}

【大数据课程】高途课程实践-Day03:Scala实现商品实时销售统计的更多相关文章

  1. MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"

    本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”.“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视.以下实践为个人在实际开发工作中,针对相对“大数据” ...

  2. WOT干货大放送:大数据架构发展趋势及探索实践分享

      WOT大数据处理技术分会场,PingCAP CTO黄东旭.易观智库CTO郭炜.Mob开发者服务平台技术副总监林荣波.宜信技术研发中心高级架构师王东及商助科技(99Click)顾问总监郑泉五位讲师, ...

  3. 大数据量高并发的数据库优化详解(MSSQL)

    转载自:http://www.jb51.net/article/71041.htm 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能. ...

  4. 网易大数据平台的Spark技术实践

    网易大数据平台的Spark技术实践 作者 王健宗 网易的实时计算需求 对于大多数的大数据而言,实时性是其所应具备的重要属性,信息的到达和获取应满足实时性的要求,而信息的价值需在其到达那刻展现才能利益最 ...

  5. 王家林的81门一站式云计算分布式大数据&移动互联网解决方案课程第14门课程:Android软硬整合设计与框架揭秘: HAL&Framework &Native Service &App&HTML5架构设计与实战开发

    掌握Android从底层开发到框架整合技术到上层App开发及HTML5的全部技术: 一次彻底的Android架构.思想和实战技术的洗礼: 彻底掌握Andorid HAL.Android Runtime ...

  6. IT大数据服务管理高级课程(IT服务,大数据,云计算,智能城市)

    个人简历 金石先生是马克思主义中国化的研究学者,上海财经大学经济学和管理学硕士,中国民主建国会成员,中国特色社会主义人文科技管理哲学的理论奠基人之一.金石先生博学多才,对问题有独到见解.专于工作且乐于 ...

  7. 大数据量高并发的数据库优化,sql查询优化

    一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...

  8. DB开发之大数据量高并发的数据库优化

    一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...

  9. 大数据量高并发访问SQL优化方法

    保证在实现功能的基础上,尽量减少对数据库的访问次数:通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担:能够分开的操作尽量分开处理,提高每次的响应速度:在数据窗口使用SQL时,尽量把 ...

  10. windows下大数据开发环境搭建(3)——Scala环境搭建

    一.所需环境 ·Java 8   二.下载Scala https://www.scala-lang.org/download/ 三.配置环境变量 SCALA_HOME: C:\scala Path: ...

随机推荐

  1. Kubernetes Operator: CRD

    Custom Resource Define 简称 CRD,是 Kubernetes(v1.7+)为提高可扩展性,让开发者去自定义资源的一种方式.CRD 资源可以动态注册到集群中,注册完毕后,用户可以 ...

  2. MySQL 中 datetime 和 timestamp 的区别与选择

    MySQL 中常用的两种时间储存类型分别是datetime和 timestamp.如何在它们之间选择是建表时必要的考虑.下面就谈谈他们的区别和怎么选择. 1 区别 1.1 占用空间 类型 占据字节 表 ...

  3. CentOS7下yum安装GitLab-CE

    前提准备 建立git用户 useradd git 关闭防火墙 systemctl stop firewalld systemctl disable firewalld 安装依赖库 yum instal ...

  4. Dockerfile 和 docker-compose.yml的区别

    明确概念 Dockerfile 是拿来构建自定义镜像的,并没有直接生成容器.只是可以在运行镜像时运行容器而已. 做容器编排以部署环境,是使用 docker-compose.yml 文件进行的,里面可能 ...

  5. iOS Social和Accounts简单使用

    ACAccountStore *account = [[ACAccountStore alloc] init]; ACAccountType *type = [account accountTypeW ...

  6. 220501 T1 困难的图论 (tarjan 点双)

    求满足题目要求的简单环,做出图中所有的点双,用vector存储点双中的边,如果该点双满足点数=边数,就是我们想要的,求边的异或和即可:如果该点双点数小于边数,说明有不只一个环覆盖,不满足题意. 1 # ...

  7. Docker容器虚拟化

    Docker容器虚拟化 目录 Docker容器虚拟化 虚拟化网络 单节点容器间通信 不同节点容器间通信 虚拟化网络 Network Namespace 是 Linux 内核提供的功能,是实现网络虚拟化 ...

  8. Oracle安装和卸载

    Oracle安装: 1. 检查是否安装net framework 3.5 2. 安装win64_11gR2_database服务端 更改安装目录,设置密码 2. 检查服务 services.msc,两 ...

  9. Docker | dockerfile构建centos镜像,以及CMD和ENTRYPOINT的区别

    构建自己的centos镜像 docker pull centos下载下来的镜像都是基础版本,缺少很多常用的命令功能,比如:ll.vim等等, 下面介绍制作一个功能较全的自己的centos镜像. 步骤 ...

  10. map集合类型/实体类类型的参数

    map集合类型的参数 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合, 将这些数据放在map中 只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要 ...