1.使用场景:
收集业务系统数据–>数据处理–>放入 OLTP 数据–>外部通过 ECharts 获取并处理数据
2.StructuredStreaming的落地问题:
在 Structured Streaming 中, 并未提供完整的 MySQL/JDBC 整合工具
不止 MySQL 和 JDBC, 可能会有其它的目标端需要写入
很多时候 Structured Streaming 需要对接一些第三方的系统, 例如阿里云的云存储, 亚马逊云的云存储等, 但是 Spark 无法对所有第三方都提供支持, 有时候需要自己编写
3.解决方式: foreach模式
既然无法满足所有的整合需求, StructuredStreaming 提供了 Foreach, 可以拿到每一个批次的数据
通过 Foreach 拿到数据后, 可以通过自定义写入方式, 从而将数据落地到其它的系统
4.代码实现
步骤:
创建类实现foreachWriter,实现三个方法
在writeStream中指定foreach(自定义类)
object foreach {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("hdfs").master("local[4]").getOrCreate()
import spark.implicits._
spark.sparkContext.setLogLevel("warn")
//1.从kafka中读取数据
val ds: Dataset[String] = spark.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "node01:9092,node02:9092,node03:9092")
.option("subscribe", "streaming_test_01")
.load()
.selectExpr("cast(value as String) as value")
.as[String]
//2.处理数据
//数据格式: 1::叶凡::中洲
val df: DataFrame = ds.map(item => {
val arr = item.split("::")
(arr(0).toInt, arr(1).toString, arr(2).toString)
}).as[(Int, String, String)].toDF("id", "name", "address")
**//3.自定义落地位置foreach--落地到MySQL中
//3.1 自定义ForeachWriter
class MySQLWriter extends ForeachWriter[Row]{
var connection:Connection = _
var statement: PreparedStatement = _
val username = "root"
val password = "123"
//启动时调用的方法获取连接
override def open(partitionId: Long, version: Long): Boolean = {
Class.forName("com.jdbc.mysql.Driver")
connection = DriverManager.getConnection("jdbc:mysql://node03:3306/test",username,password)
//这里最好不要使用createStatement,如果原始数据中有单引号 ' ,在sql语句运行的时候会报错
statement = connection.prepareStatement("insert into test01 values(?,?,?)")
//返回true触发下面的方法执行
true
}
//当open()返回true的时候调用
override def process(value: Row): Unit = {
val id: Int = value.getAs[String]("id").toInt
val name = value.getAs[String]("name")
val catogry = value.getAs[String]("catogry")
statement.setInt(1,id)
statement.setString(2,name)
statement.setString(3,catogry)
statement.execute()
}
//关闭的时候调用
override def close(errorOrNull: Throwable): Unit = {
statement.close()
connection.close()
}
}**
//启动写入
df.writeStream
.foreach(new MySQLWriter)
.start()
.awaitTermination()
}
}
注意:
foreachWriter处理的数据是ROW类型,效率比较低
2.4版本的foreachBatch按批处理是一种更高效的模式