在任务1进行的同时,使用侧边流,监控若发现order_status字段为退回完成, 将key设置成totalrefundordercount存入Redis中,value存放用户退款消费额。使用redis cli以get key方式获取totalrefundordercount值,将结果截图粘贴至客户端桌面【Release任务D提交结果.docx】中对应的任务序号下,需两次截图,第一次截图和第二次截图间隔1分钟以上,第一次截图放前面,第二次截图放后面;
使用Flink消费Kafka中的数据,统计商城实时订单实收金额(需要考虑订单状态,若有取消订单、申请退回、退回完成则不计入订单实收金额,其他状态的则累加),将key设置成totalprice存入Redis中。使用redis cli以get key方式获取totalprice值,将结果截图粘贴至客户端桌面【Release任务D提交结果.docx】中对应的任务序号下,需两次截图,第一次截图和第二次截图间隔1分钟以上,第一次截图放前面,第二次截图放后面;
--1.2. 获取Flink流运行环境
在flink工程目录的src/main/scala/org/example目录下新建TotalPrice.scala文件,新建object对象和main函数,在主方法中获取Flink流运行环境,代码如下:
package org.example
import org.apache.flink.api.common.eventtime.{SerializableTimestampAssigner, WatermarkStrategy}
import org.apache.flink.connector.kafka.source.KafkaSource
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer
import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.streaming.api.scala._
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.util.serialization.SimpleStringSchema
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment
import org.apache.flink.util.Collector
import java.text.SimpleDateFormat
import java.time.Duration
object TotalPrice {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
}
}
--1.3. 定义Kafka源
在main函数中定义kafka源,消费kafka的order主题,代码如下:
val source = KafkaSource.builder[String].
setBootstrapServers("localhost:9092").
setTopics("order").
setGroupId("order1").
setStartingOffsets(OffsetsInitializer.earliest()).
setValueOnlyDeserializer(new SimpleStringSchema()).build()
--1.4. 创建流
在main函数中使用kafka数据源创建flink流,代码如下:
val stream = env.fromSource(source, WatermarkStrategy.noWatermarks[String], "Kafka Source")
--1.5. 定义订单数量统计侧边流标签
在main函数中定义订单金额统计侧边流标签,通过标签可以写出侧边流和获取侧边流,代码如下:
val totalprice = OutputTag[(String, String)]("totalprice")
--1.6. 设置水位线
设置水位线
根据题目要求,允许数据延迟5s,那么我们就需要设置水位线超时时间为5s,代码如下:
val newstream = stream.assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness[String](Duration.ofSeconds(5))
--1.7. 设置事件时间
根据题目要求,计算中使用order_info或order_detail表中create_time或operate_time取两者中值较大者作为EventTime,所以我们需要自定义事件时间为create_time或operate_time较大的时间,代码(使用链式编程接1.6步骤的末尾)如下:
.withTimestampAssigner(
new SerializableTimestampAssigner[String] {
override def extractTimestamp(t: String, l: Long):
Long = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(if (t.split(",")(12) > t.split(",")(11)) t.split(",")(12) else t.split(",")(11)).getTime
}
))
--1.8. 自定义处理函数
根据题目要求,统计商城实时订单金额(需要考虑订单状态,若有取消订单、申请退回、退回完成则不计入订单金额,其他状态则累加),需要在处理函数中将不是取消订单、申请退回、退回完成的订单输出到侧边流,主流中的数据不作处理,调用collector收集即可。码(使用链式编程接1.6步骤的末尾)如下:
.process(new ProcessFunction[String, String] {
override def processElement(i: String, context: ProcessFunction[String, String]#Context, collector: Collector[String]): Unit = {
println("getdataQ" + i)
val orderstatus = i.split(",")(5)
println("orderstatus" + orderstatus)
val orderprice = i.split(",")(3)
if (!orderstatus.equals("1003") && !orderstatus.equals("1005") && !orderstatus.equals("1006")) {
context.output(totalprice, ("totalprice", orderprice))
}
collector.collect(i)
}
})
--1.9. 统计订单金额
获取侧边流,通过map转换根据key统计出订单的金额,代码如下:
val newstream2 = newstream.getSideOutput(totalprice).map(t => ("totalprice", String.valueOf(t._2).toDouble)).keyBy(t => t._1).sum(1).map(t => {
println("redisdata" + String.valueOf(t._2))
String.valueOf(t._2)
}
)
--1.10. 创建redis连接池
根据题目要求,统计的结果需要保存到redis中,需要建立redis的连接池,代码如下:
val conf = new FlinkJedisPoolConfig.Builder().setHost("localhost").build()
--1.11. 创建订单统计Redis映射器
由于订单的统计结果是在flink中,我们要把结果写入到redis,那么redis就需要知道数据是什么格式,key是什么,value是什么,redis执行的命令是什么,这就需要定义一个映射器来实现,代码如下:
// 实现RedisMapper接口
class MyRedisMapper2 extends RedisMapper[String] {
override def getCommandDescription: RedisCommandDescription = new RedisCommandDescription(RedisCommand.SET, "totalprice")
override def getKeyFromData(t: String): String = "totalprice"
override def getValueFromData(t: String): String = t
}
--1.12. 订单统计结果写入redis
通过侧边流设置redis的连接池信息和映射器信息就可以将数据保存到redis,代码如下:
newstream2.addSink(new RedisSink[String](conf, new MyRedisMapper2))
--1.13. 启动任务
代码编写好以后,需要调用流运行环境来执行任务,代码如下:
env.execute("Job")
--1.14. 打包代码
在Linux终端执行如下命令,使用maven打包代码
cd /rgsoft/Desktop/Study/task/
/opt/apache-maven-3.9.1/bin/mvn clean
/opt/apache-maven-3.9.1/bin/mvn install
--1.15. 提交代码到集群
在Linux终端执行如下命令,提交jar包到集群
echo "classloader.check-leaked-classloader: false" >> /opt/flink/conf/flink-conf.yaml
export HADOOP_CLASSPATH=`hadoop classpath`
/opt/flink/bin/flink run -t yarn-per-job --detached -c org.example.TotalPrice ./target/flink-1.0-SNAPSHOT.jar
--1.16. 查看任务结果
在redis终端查看写入的结果数据,执行如下命令连接redis,然后查看结果
# 连接redis
redis-cli
# 在redis命令行执行命令
keys *
get totalprice
quit
在任务1进行的同时,使用侧边流,监控若发现order_status字段为退回完成, 将key设置成totalrefundordercount存入Redis中,value存放用户退款消费额。使用redis cli以get key方式获取totalrefundordercount值,将结果截图粘贴至客户端桌面【Release任务D提交结果.docx】中对应的任务序号下,需两次截图,第一次截图和第二次截图间隔1分钟以上,第一次截图放前面,第二次截图放后面;
--2.2. 获取Flink流运行环境
在flink工程目录的src/main/scala/org/example目录下新建TotalRefundOrderCount.scala文件,新建object对象和main函数,在主方法中获取Flink流运行环境,代码如下:
package org.example
import org.apache.flink.api.common.eventtime.{SerializableTimestampAssigner, WatermarkStrategy}
import org.apache.flink.connector.kafka.source.KafkaSource
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer
import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
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.util.serialization.SimpleStringSchema
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment
import org.apache.flink.util.Collector
import java.text.SimpleDateFormat
import java.time.Duration
object TotalRefundOrderCount {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
}
}
--2.3. 定义Kafka源
在main函数中定义kafka源,消费kafka的order主题,代码如下:
val source = KafkaSource.builder[String].
setBootstrapServers("localhost:9092").
setTopics("order").
setGroupId("order2").
setStartingOffsets(OffsetsInitializer.earliest()).
setValueOnlyDeserializer(new SimpleStringSchema()).build()
--2.4. 创建流
在main函数中使用kafka数据源创建flink流,代码如下:
val stream = env.fromSource(source, WatermarkStrategy.noWatermarks[String], "Kafka Source")
--2.5. 定义侧边流标签
在main函数中定义订单金额统计侧边流标签和退单统计侧边流标签,通过标签可以写出侧边流和获取侧边流,代码如下:
val totalprice=OutputTag[(String,String)]("totalprice")
val totalrefundordercount=OutputTag[(String,String)]("totalrefundordercount")
--2.6. 设置水位线
根据题目要求,允许数据延迟5s,那么我们就需要设置水位线超时时间为5s,代码如下:
val newstream= stream.assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness[String](Duration.ofSeconds(5))
--2.7. 设置事件时间
根据题目要求,计算中使用order_info或order_detail表中create_time或operate_time取两者中值较大者作为EventTime,所以我们需要自定义事件时间为create_time或operate_time较大的时间,代码(使用链式编程接1.6步骤的末尾)如下:
.withTimestampAssigner(
new SerializableTimestampAssigner[String] {
override def extractTimestamp(t: String, l: Long):
Long = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(if (t.split(",")(12) > t.split(",")(11)) t.split(",")(12) else t.split(",")(11)).getTime
}
))
--2.8. 自定义处理函数
根据题目要求,统计商城实时订单金额(需要考虑订单状态,若有取消订单、申请退回、退回完成则不计入订单金额,其他状态则累加),需要在处理函数中将不是取消订单、申请退回、退回完成的订单输出到侧边流,另外还需要将退回完成的订单输出到侧边流进行统计,主流中的数据不作处理,调用collector收集即可。码(使用链式编程接2.6步骤的末尾)如下:
.process(new ProcessFunction[String,String]{
override def processElement(i: String, context: ProcessFunction[String, String]#Context, collector: Collector[String]): Unit ={
println("getdataQ"+i)
val orderstatus=i.split(",")(5)
println("orderstatus"+orderstatus)
val orderprice=i.split(",")(3)
if(!orderstatus.equals("1003") && !orderstatus.equals("1005") && !orderstatus.equals("1006")){
context.output(totalprice,("totalprice",orderprice))
}
if (orderstatus.equals("1006")) {
context.output(totalrefundordercount, ("totalrefundordercount",orderprice))
}
collector.collect(i)
}
})
--2.9. 统计订单金额
获取侧边流,通过map转换根据key统计出订单的金额,代码如下:
val newstream2=newstream.getSideOutput(totalprice).map(t=>("totalprice",String.valueOf(t._2).toDouble)).keyBy(t=>t._1).sum(1).map(t=>{
println("redisdata" +String.valueOf(t._2))
String.valueOf(t._2)}
)
--2.10. 统计退回完成的订单的金额
获取侧边流,通过map转换根据key统计出订单的金额,代码如下:
val newstream3=newstream.getSideOutput(totalrefundordercount).map(t=>("totalrefundordercount",String.valueOf(t._2).toDouble)).keyBy(t=>t._1).sum(1).map(t=>String.valueOf(t._2))
--2.11. 创建redis连接池
根据题目要求,统计的结果需要保存到redis中,需要建立redis的连接池,代码如下:
val conf = new FlinkJedisPoolConfig.Builder().setHost("localhost").build()
--2.12. 创建订单统计Redis映射器
由于订单的统计结果是在flink中,我们要把结果写入到redis,那么redis就需要知道数据是什么格式,key是什么,value是什么,redis执行的命令是什么,这就需要定义一个映射器来实现,代码如下:
// 实现RedisMapper接口
class MyRedisMapper2 extends RedisMapper[String] {
override def getCommandDescription: RedisCommandDescription = new RedisCommandDescription(RedisCommand.SET, "totalprice")
override def getKeyFromData(t: String): String = "totalprice"
override def getValueFromData(t: String): String = t
}
--2.13. 创建退单统计Redis映射器
由于退单统计的结果是在flink中,我们要把结果写入到redis,那么redis就需要知道数据是什么格式,key是什么,value是什么,redis执行的命令是什么,这就需要定义一个映射器来实现,代码如下:
// 实现RedisMapper接口
class MyRedisMapper3 extends RedisMapper[String] {
override def getCommandDescription: RedisCommandDescription = new RedisCommandDescription(RedisCommand.SET, "totalrefundordercount")
override def getKeyFromData(t: String): String = "totalrefundordercount"
override def getValueFromData(t: String): String = t
}
--2.14. 订单统计结果写入redis
通过侧边流设置redis的连接池信息和映射器信息就可以将数据保存到redis,代码如下:
newstream2.addSink(new RedisSink[String](conf, new MyRedisMapper2))
--2.15. 退单统计结果写入redis
通过侧边流设置redis的连接池信息和映射器信息就可以将数据保存到redis,代码如下:
newstream3.addSink(new RedisSink[String](conf, new MyRedisMapper3))
--2.16. 启动任务
代码编写好以后,需要调用流运行环境来执行任务,代码如下:
env.execute("Job")
--2.17. 打包代码
在Linux终端执行如下命令,使用maven打包代码
cd /rgsoft/Desktop/Study/task/
/opt/apache-maven-3.9.1/bin/mvn clean
/opt/apache-maven-3.9.1/bin/mvn install
--2.18. 提交代码到集群
在Linux终端执行如下命令,提交jar包到集群
export HADOOP_CLASSPATH=`hadoop classpath`
/opt/flink/bin/flink run -t yarn-per-job --detached -c org.example.TotalRefundOrderCount ./target/flink-1.0-SNAPSHOT.jar
--2.19. 查看任务结果
在redis终端查看写入的结果数据,执行如下命令连接redis,然后查看结果
# 连接redis
redis-cli
# 在redis命令行执行命令
keys *
get totalrefundordercount
quit
在任务1进行的同时,使用侧边流,监控若发现order_status字段为取消订单,将数据存入MySQL数据库shtd_result的order_info表中,然后在Linux的MySQL命令行中根据id降序排序,查询列id、consignee、consignee_tel、final_total_amount、feight_fee,查询出前5条,将SQL语句复制粘贴至客户端桌面【Release任务D提交结果.docx】中对应的任务序号下,将执行结果截图粘贴至客户端桌面【Release任务D提交结果.docx】中对应的任务序号下。
在flink工程目录的src/main/scala/org/example目录下新建CancelData_2_Mysql.scala文件,新建object对象和main函数,在主方法中获取Flink流运行环境,代码如下:
package org.example
import org.apache.flink.api.common.eventtime.{SerializableTimestampAssigner, WatermarkStrategy}
import org.apache.flink.connector.jdbc.{JdbcConnectionOptions, JdbcExecutionOptions, JdbcSink, JdbcStatementBuilder}
import org.apache.flink.connector.kafka.source.KafkaSource
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer
import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.streaming.api.scala._
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.util.serialization.SimpleStringSchema
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment
import org.apache.flink.util.Collector
import java.sql.PreparedStatement
import java.text.SimpleDateFormat
import java.time.Duration
object CancelData_2_Mysql {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
}
}
--3.3. 定义Kafka源
在main函数中定义kafka源,消费kafka的order主题,代码如下:
val source = KafkaSource.builder[String].
setBootstrapServers("localhost:9092").
setTopics("order").
setGroupId("order3").
setStartingOffsets(OffsetsInitializer.earliest()).
setValueOnlyDeserializer(new SimpleStringSchema()).build()
--3.4. 创建流
在main函数中使用kafka数据源创建flink流,代码如下:
val stream = env.fromSource(source, WatermarkStrategy.noWatermarks[String], "Kafka Source")
--3.5. 定义侧边流标签
在main函数中定义订单金额统计侧边流标签和取消订单侧边流标签,通过标签可以写出侧边流和获取侧边流,代码如下:
//使用侧边流
val totalprice = OutputTag[(String, String)]("totalprice")
val canceldata = OutputTag[(String, String)]("canceldata")
--3.6. 设置水位线
根据题目要求,允许数据延迟5s,那么我们就需要设置水位线超时时间为5s,代码如下:
val newstream = stream.assignTimestampsAndWatermarks(WatermarkStrategy.forBoundedOutOfOrderness[String](Duration.ofSeconds(5))
--3.7. 设置事件时间
根据题目要求,计算中使用order_info或order_detail表中create_time或operate_time取两者中值较大者作为EventTime,所以我们需要自定义事件时间为create_time或operate_time较大的时间,代码(使用链式编程接1.6步骤的末尾)如下:
.withTimestampAssigner(
new SerializableTimestampAssigner[String] {
override def extractTimestamp(t: String, l: Long):
Long = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(if (t.split(",")(12) > t.split(",")(11)) t.split(",")(12) else t.split(",")(11)).getTime
}
))
--3.8. 自定义处理函数
根据题目要求,统计商城实时订单金额(需要考虑订单状态,若有取消订单、申请退回、退回完成则不计入订单数量,其他状态则累加),需要在处理函数中将不是取消订单、申请退回、退回完成的订单输出到侧边流,另外还需要将取消订单输出到侧边流,主流中的数据不作处理,调用collector收集即可。码(使用链式编程接3.6步骤的末尾)如下:
.process(new ProcessFunction[String, String] {
override def processElement(i: String, context: ProcessFunction[String, String]#Context, collector: Collector[String]): Unit = {
println("getdataQ" + i)
val orderstatus = i.split(",")(5)
println("orderstatus" + orderstatus)
val orderprice = i.split(",")(3)
if (!orderstatus.equals("1003") && !orderstatus.equals("1005") && !orderstatus.equals("1006")) {
context.output(totalprice, ("totalprice", orderprice))
}
if (orderstatus.equals("1003")) {
context.output(canceldata, ("canceldata", i))
}
collector.collect(i)
}
})
--3.9. 统计订单数量
获取侧边流,通过map转换根据key统计出订单的数量,代码如下:
val newstream2 = newstream.getSideOutput(totalprice).map(t => ("totalprice", String.valueOf(t._2).toDouble)).keyBy(t => t._1).sum(1).map(t => {
println("redisdata" + String.valueOf(t._2))
String.valueOf(t._2)
}
)
--3.10. 取消订单的数据重新写回MySQL
获取侧边流,将数据写回MySQL,代码如下:
val newstream4 = newstream.getSideOutput(canceldata).map(t => t._2)
newstream4.addSink(JdbcSink.sink[String](
"insert into shtd_result.order_info values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
new JdbcStatementBuilder[String] {
override def accept(t: PreparedStatement, u: String) = {
val strarr = u.split(",")
for (i <- 0 to strarr.size - 1) {
if (i == 0) {
t.setInt(i+1, String.valueOf(strarr(0)).toInt)
} else {
t.setString(i+1, String.valueOf(strarr(i)))
}
}
}
},
JdbcExecutionOptions.builder()
.withBatchIntervalMs(100)
.withBatchSize(5)
.withMaxRetries(0)
.build(),
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://127.0.0.1:3306/shtd_result?useSSL=false&useUnicode=true&characterEncoding=utf8")
.withDriverName("com.mysql.jdbc.Driver")
.withUsername("root1")
.withPassword("123456")
.build()
))
--3.11. 创建redis连接池
根据题目要求,统计的结果需要保存到redis中,需要建立redis的连接池,代码如下:
val conf = new FlinkJedisPoolConfig.Builder().setHost("localhost").build()
--3.12. 创建订单统计Redis映射器
由于订单的统计结果是在flink中,我们要把结果写入到redis,那么redis就需要知道数据是什么格式,key是什么,value是什么,redis执行的命令是什么,这就需要定义一个映射器来实现,代码如下:
// 实现RedisMapper接口
class MyRedisMapper2 extends RedisMapper[String] {
override def getCommandDescription: RedisCommandDescription = new RedisCommandDescription(RedisCommand.SET, "totalprice")
override def getKeyFromData(t: String): String = "totalprice"
override def getValueFromData(t: String): String = t
}
--3.13. 订单统计结果写入redis
通过侧边流设置redis的连接池信息和映射器信息就可以将数据保存到redis,代码如下:
newstream2.addSink(new RedisSink[String](conf, new MyRedisMapper2))
--3.14. 启动任务
代码编写好以后,需要调用流运行环境来执行任务,代码如下:
env.execute("Job")
--3.15. 打包代码
在Linux终端执行如下命令,使用maven打包代码
cd /rgsoft/Desktop/Study/task/
/opt/apache-maven-3.9.1/bin/mvn clean
/opt/apache-maven-3.9.1/bin/mvn install
--3.16. 提交代码到集群
在Linux终端执行如下命令,提交jar包到集群
export HADOOP_CLASSPATH=`hadoop classpath`
/opt/flink/bin/flink run -t yarn-per-job --detached -c org.example.CancelData_2_Mysql ./target/flink-1.0-SNAPSHOT.jar
--3.17. 查看任务结果
在MySQL终端查看写入的结果数据,执行如下命令连接MySQL,然后查看结果
# 连接mysql
mysql -uroot -p123456
# 在mysql命令行执行命令
select id,consignee,consignee_tel,final_total_amount,feight_fee from shtd_result.order_info order by id desc limit 5;
exit