Flink实战4-Flink广播流动态更新MySQL_Source配置信息实现配置化流式处理程序

9 篇文章 2 订阅
6 篇文章 3 订阅

背景

  • 适用于配置化操作流,无需终止流式程序实现配置,并且以广播流的形式在流式程序中使用;
  • 实现MySQL_Source配置信息动态定时更新;
  • 实现MySQL_Source广播流,此处使用最常用的keyby广播流KeyedBroadcastProcessFunction;

摘要

关键字

  • MySQL_Source、Flink广播流;

设计

  • MyJdbcSource

    1. 日常创建一个继承源富函数的类;
    2. 初始化单连接;
    3. 配置更新时间设置自己可以编写方法应用;
  • 配置化Flink广播流;

    1. 获取配置Source随即进行broadcast;

    2. 接入数据流进行数据操作;

    3. 结果数据流与广播流连接connect,与多数据流connect底层原理类似,最底层每个core对应一个广播流进行高效匹配;

      在这里插入图片描述

    4. process,按照类重写方法分别进行处理数据流和广播流;

理解

  • eg:网路搬来的图片,自己就不花时间画啦;

    1. 广播流

      img

    2. 普通数据流

      img

实现

说明

此处的处理没有写成项目中使用的比较复杂的可配置化的形式,也就是只针对单表测试表的操作;

依赖

<scala.main.version>2.11</scala.main.version>
<flink.version>1.10.1</flink.version>
<!--flink-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-scala_${scala.main.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-scala_${scala.main.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka_${scala.main.version}</artifactId>
            <version>1.10.1</version>
        </dependency>

        <!--flink table & sql-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-scala-bridge_${scala.main.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-planner_${scala.main.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <!--导入flink连接redis的文件-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-redis_${scala.main.version}</artifactId>
            <version>${flink.redis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-elasticsearch6_${scala.main.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <!--rocksdb 与flink 进行整合依赖-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-statebackend-rocksdb_2.11</artifactId>
            <version>1.9.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-cep-scala_2.11</artifactId>
            <version>${flink.version}</version>
        </dependency>

main

import java.util

import com.xx.beans.FlinkMergeDataResultBean
import com.xx.utils.flink.soruce.{MyJdbcSource, MyKafkaSource}
import com.xx.utils.flink.states.ValueStateDemo.MyMapperTableData
import org.apache.flink.api.common.state.{BroadcastState, MapStateDescriptor, ReadOnlyBroadcastState}
import org.apache.flink.api.common.typeinfo.BasicTypeInfo
import org.apache.flink.api.java.typeutils.MapTypeInfo
import org.apache.flink.streaming.api.datastream.BroadcastStream
import org.apache.flink.streaming.api.{CheckpointingMode, TimeCharacteristic}
import org.apache.flink.streaming.api.functions.co.{KeyedBroadcastProcessFunction, KeyedCoProcessFunction}
import org.apache.flink.streaming.api.scala.{BroadcastConnectedStream, DataStream, StreamExecutionEnvironment}
import org.apache.flink.util.Collector

import scala.collection.mutable

/**
 * @Author KevinLu
 * @Description Flink实现动态MySQL数据配置广播流
 * @Copyright 代码类版权的最终解释权归属KevinLu本人所有;
 **/
object BroadcastStateStreamDemo {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    /**
     * 重启策略配置
     */


    /**
     * import
     */
    import org.apache.flink.api.scala._

    /**
     * 获取配置MySQL流
     */
    val propertiesStream: DataStream[mutable.HashMap[String, String]] = env.addSource(MyJdbcSource)

    /**
     * * @param name {@code MapStateDescriptor}的名称。
     * * @param keyTypeInfo状态下键的类型信息。
     * * @param valueTypeInfo状态值的类型信息。
     */
    val mapStateDescriptor: MapStateDescriptor[String, util.Map[String, String]] = new MapStateDescriptor("broadCastConfig", BasicTypeInfo.STRING_TYPE_INFO, new MapTypeInfo(classOf[String], classOf[String]))
    //拿到配置广播流
    val broadcastStream: BroadcastStream[mutable.HashMap[String, String]] = propertiesStream.setParallelism(1).broadcast(mapStateDescriptor)
    //数据流
    val dStream1: DataStream[String] = MyKafkaSource.myKafkaSource(
      env,
      "xxx:9092,xxx:9092,xxx:9092",
      List("xxx")
    )

    val keydStream = dStream1
      .map(new MyMapperTableData)
      .keyBy(_.key)

    val broadcastConnectedStream: BroadcastConnectedStream[FlinkMergeDataResultBean, mutable.HashMap[String, String]] = keydStream.connect(broadcastStream)

    /**
     * * @param <KS>输入的关键字流的关键类型。KeyedStream 中 key 的类型
     * * @param <IN1>键控(非广播)端的输入类型。
     * * @param <IN2>广播端输入类型。
     * * @param <OUT>操作符的输出类型。
     */
    broadcastConnectedStream.process(
      new KeyedBroadcastProcessFunction[String, FlinkMergeDataResultBean, mutable.HashMap[String, String], String] {
        val mapStateDescriptor : MapStateDescriptor[String, util.Map[String, String]] = new MapStateDescriptor("broadCastConfig", BasicTypeInfo.STRING_TYPE_INFO, new MapTypeInfo(classOf[String], classOf[String]))
        /**
         * 初始化
         */
        private var hashMap: mutable.HashMap[String, String] = mutable.HashMap[String, String]()
        /**
         * 这个函数处理数据流的数据,这里之只能获取到 ReadOnlyBroadcastState,因为 Flink 不允许在这里修改 BroadcastState 的状态。
         * value 是数据流中的一个元素;ctx 是上下文,可以提供计时器服务、当前 key和只读的 BroadcastState;out 是输出流收集器。
         *
         * @param value
         * @param ctx
         * @param out
         */
        override def processElement(value: FlinkMergeDataResultBean, ctx: KeyedBroadcastProcessFunction[String, FlinkMergeDataResultBean, mutable.HashMap[String, String], String]#ReadOnlyContext, out: Collector[String]): Unit = {
//          val broadcastValue: HeapBroadcastState[String, util.Map[String, String]] = ctx.getBroadcastState(mapStateDescriptor).asInstanceOf[HeapBroadcastState[String, util.Map[String, String]]]
          println("hashMap",hashMap)
          println("value",value)
          println("hashMap.get(value.key)",hashMap.get(value.key))
          out.collect(hashMap.get(value.key).getOrElse("NU"))
        }

        /**
         * 这里处理广播流的数据,将广播流数据保存到 BroadcastState 中。
         * @param value value 是广播流中的一个元素;
         * @param ctx ctx 是上下文,提供 BroadcastState 和修改方法;
         * @param out out 是输出流收集器。
         */
        override def processBroadcastElement(value: mutable.HashMap[String, String], ctx: KeyedBroadcastProcessFunction[String, FlinkMergeDataResultBean, mutable.HashMap[String, String], String]#Context, out: Collector[String]): Unit = {
//          val broadcastState: BroadcastState[String, util.Map[String, String]] = ctx.getBroadcastState(mapStateDescriptor)
          hashMap = value
        }
      }
    ).print("输出结果:")

    env.execute()
  }
}

Bean

case class FlinkMergeDataResultBean(
                                   key : String ,
                                   data : JSONObject
                                   )

MySQL_Source

import java.sql.Connection

import com.xx.contant.ContantCommon
import com.xx.utils.jdbc.Jdbc
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.functions.source.{RichSourceFunction, SourceFunction}

import scala.collection.mutable

/**
 * Description:xxxx<br/>
 * Copyright (c) ,20xx , KevinLu <br/>
 * This program is protected by copyright laws. <br/>
 *
 * @author KevinLu
 * @version : 1.0
 */
object MyJdbcSource extends RichSourceFunction[mutable.HashMap[String, String]] {
  private var jdbcConn: Connection = null
  val url = "jdbc:mysql://xx:3306/xx?user=xx&password=xx&characterEncoding=UTF-8&useSSL=false"
  var isRunning = true

  /**
   * 初始化source
   * @param parameters
   */
  override def open(parameters: Configuration): Unit = {
    println(s"查询MySQL已经建立连接!")
    //所有Jdbc单连接方法都可,根据获取情况判定是否使用连接池
    jdbcConn = Jdbc.getConnect(url)
  }

  override def run(ctx: SourceFunction.SourceContext[mutable.HashMap[String, String]]): Unit = {
    val sql = s"select name , properties from properties"
    /**
     * 收到同一个source_table下的多个task_name任务
     */
    var mapResult = mutable.HashMap[String, String]()
    while(isRunning){
      /**
       * 收到同一个source_table下的多个task_name任务
       */
      val map = mutable.HashMap[String, String]()
      try {
        val stmt = jdbcConn.createStatement()
        val pstmt = stmt.executeQuery(sql)
        while (pstmt.next) {
          /**
           * 双MAP做唯一判断
           */
          map.put(pstmt.getString(1), pstmt.getString(2))
        }
      } catch {
        case e: Exception => println(s"${this.getClass.getSimpleName}-查询源数据配置信息异常 → ${e}", e.printStackTrace())
      }
      mapResult = map
      ctx.collect(mapResult)
      Thread.sleep(10000)
    }
  }

  /**
   * 取消一个job时
   */
  //  override def cancel(): Unit = {
  //    if (jdbcConn != null) {
  //      jdbcConn.close()
  //    } else {
  //      println("JDBC连接为空,请注意检查!")
  //    }
  //  }

  override def close(): Unit = {
    jdbcConn.close()
    println("mysql连接已经关闭;")
  }

  override def cancel(): Unit = {
    isRunning = false
  }
}

Kafka_Source

import java.util.Properties

import com.xxxx.contant.Contant
import org.apache.flink.api.common.serialization.{DeserializationSchema, SimpleStringSchema}
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.connectors.kafka.{FlinkKafkaConsumer}

/**
 * @Author KevinLu(鹿)
 * @Description ******
 * @Date xxx
 * @Copyright 代码类版权的最终解释权归属KevinLu本人所有;
 **/
object MyKafkaSource {
  def myKafkaSource(
                     env : StreamExecutionEnvironment ,
                     bootstrapServers : String ,
                     topics : List[String]
                   ): DataStream[String] ={
    /**
     * 获取基础参数
     */
    import org.apache.flink.api.scala._
    import scala.collection.JavaConversions._
    /**
     * 定义kafka-source得到DataStream
     */
    //将kafka中数据反序列化,
    val valueDeserializer: DeserializationSchema[String] = new SimpleStringSchema()

    val properties = new Properties()
    properties.put("bootstrap.servers", bootstrapServers)

    val kafkaSinkDStream: DataStream[String] = env.addSource(new FlinkKafkaConsumer[String](topics, valueDeserializer, properties))
    kafkaSinkDStream
  }
}

Map_Function

  /**
   * 自定义mapper处理相关数据
   * 判断如果是相关宽表中的数据那么分为同一个by中
   */
  class MyMapperTableData() extends RichMapFunction[String , FlinkMergeDataResultBean] {

    override def map(in: String): FlinkMergeDataResultBean = {
      println(s"source data:$in")
      val json = JSON.parseObject(in)
      FlinkMergeDataResultBean(json.getString("table") , json.getJSONObject("data"))
    }
  }

部署

#!/bin/bash
flink run -m yarn-cluster \
-c xxx.xxx \
-p 8 \
/Linux根目录

注意事项

  1. 开发者根据自己需要拆解使用,包含知识点不单一,MySQL_Source、广播流、底层connect过程等;
  2. 单使用广播流可以使用外部文件、Kafka、Redis等进行测试,两个Kafka流进行测试相对容易上手;
  3. 需要花费时间深入的点就是广播流的运转模式,不同场景如何切换开发思路;
flink mysqlcdc是一个用于将MySQL数据源连接到Flink的插件。它可以实时捕获MySQL数据库中的更改,并将其作为数据进行处理和分析。引用中的代码片段展示了如何配置flink mysqlcdc作业的SQL语句。首先通过创建两个表`flink_test_order`和`flink_test_order2`来定义数据模式和字段。然后使用`mysql-cdc`连接器将`flink_test_order`表连接到MySQL数据库,并将其配置为使用指定的主机名、端口号、用户名和密码。最后,使用`print`连接器将更改的数据插入到`flink_test_order2`表中。这样,通过flink mysqlcdc,您可以实时地将MySQL数据库中的更改数据流式传输到Flink中进行进一步的处理和分析。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Flink 使用之 MySQL CDC](https://blog.csdn.net/wuxintdrh/article/details/119841434)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Flink mysql-cdc](https://blog.csdn.net/CarloPan/article/details/121441962)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值