Hudi-StructuredStreaming流式写入Hudi
场景
实时产生的数据写入到Kafka,由Spark实时读取Kafka的数据,写入hudi
实现
package com.zhen.hudi.streaming import com.zhen.hudi.didi.SparkUtils import org.apache.hudi.DataSourceWriteOptions.{PARTITIONPATH_FIELD, PRECOMBINE_FIELD, RECORDKEY_FIELD} import org.apache.hudi.config.HoodieWriteConfig.TBL_NAME import org.apache.spark.sql.functions._ import org.apache.spark.sql.streaming.OutputMode import org.apache.spark.sql.{DataFrame, Dataset, Row, SaveMode, SparkSession} /** * @Author FengZhen * @Date 3/3/22 10:16 PM * @Description 基于StructuredStreaming结构化流实时从kafka消费数据,经过ETL转换后,存储至Hudi表 */ object HudiStructureDemo { /** * 指定kafka topic名称,实时消费数据 * @param spark * @param topicName * @return */ def readFromKafka(spark: SparkSession, topicName: String): DataFrame = { spark.readStream .format("kafka") .option("kafka.bootstrap.servers", "localhost:9092") .option("subscribe", topicName) //消费位置 .option("startingOffsets", "latest") //每次最多处理十万条 .option("maxOffsetsPerTrigger", 100000) //数据丢失是否失败 .option("failOnDataLoss", "false") .load() } /** * 对kafka获取的数据进行转换操作,获取所有字段的值,转换为String,以便保存到hudi表 * @param streamDF * @return */ def process(streamDF: DataFrame): DataFrame = { streamDF //选择字段 .selectExpr( "CAST(key AS STRING) AS order_id", "CAST(value AS STRING) AS message", "topic", "partition", "offset", "timestamp" ) //解析Message数据,提取字段值 .withColumn("user_id", get_json_object(col("message"), "$.userId")) .withColumn("order_time", get_json_object(col("message"), "$.orderTime")) .withColumn("ip", get_json_object(col("message"), "$.ip")) .withColumn("order_money", get_json_object(col("message"), "$.orderMoney")) .withColumn("order_status", get_json_object(col("message"), "$.orderStatus")) //删除message字段 .drop(col("message")) //转换订单日期时间格式为Long类型,作为hudi表中合并数据字段 .withColumn("ts", to_timestamp(col("order_time"), "yyyy-MM-dd HH:mm:ss.SSS")) //订单日期时间提取分区日期:yyyy-MM-dd .withColumn("day", substring(col("order_time"), 0, 10)) } /** * 将流式数据DataFrame保存到Hudi表中 * @param streamDF */ def saveToHudi(streamDF: DataFrame): Unit = { streamDF.writeStream .outputMode(OutputMode.Append()) .queryName("query-hudi-streaming") .foreachBatch((batchDF: Dataset[Row], batchId: Long) =>{ println(s"=============== BatchId: ${batchId} start =============== ") import org.apache.hudi.DataSourceWriteOptions._ import org.apache.hudi.config.HoodieWriteConfig._ import org.apache.hudi.keygen.constant.KeyGeneratorOptions._ batchDF.write .mode(SaveMode.Append) .format("hudi") .option("hoodie.insert.shuffle.parallelism", "2") .option("hoodie.upsert.shuffle.parallelism", "2") //hudi表的属性值的设置 //主键 .option(RECORDKEY_FIELD.key(), "order_id") //预合并 .option(PRECOMBINE_FIELD.key(), "ts") //分区 .option(PARTITIONPATH_FIELD.key(), "day") //分区值对应目录格式,与hive分区策略一致 .option(HIVE_STYLE_PARTITIONING_ENABLE.key(), "true") //表名 .option(TBL_NAME.key(), "tbl_hudi_order") //读取时合并表类型 .option(TABLE_TYPE.key(), "MERGE_ON_READ") .save("/hudi-warehouse/tbl_hudi_order") }) .option("checkpointLocation", "/datas/hudi-spark/struct-ckpt-1001") .start() } def main(args: Array[String]): Unit = { //1.构建SparkSession实例对象 val spark: SparkSession = SparkUtils.createSparkSession(this.getClass) //2.从kafka实时消费数据 val kafkaStreamDF: DataFrame = readFromKafka(spark, "order-topic") //3.提取数据,转换数据类型 val streamDF: DataFrame = process(kafkaStreamDF) //4.保存数据至hudi表中:MOR类型,读取表数据合并文件 saveToHudi(streamDF) //5.流式应用启动以后,等待终止 spark.streams.active.foreach(query => println(s"Query: ${query.name} is Running")) spark.streams.awaitAnyTermination() } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
2020-03-03 机器学习-朴素贝叶斯原理及Python实现
2020-03-03 机器学习-文本聚类实例-kmeans