[ソースの分析]ソースおよびブロードキャストブロードキャストFLINKを見ての開始の例

[ソースの分析]ソースおよびブロードキャストブロードキャストFLINKを見ての開始の例

0x00の概要

本論文では、おなじみのFLINK放送可変機構を主導し、説明するためのソースコードと例を分析します。

0x01のビジネスニーズ

1.シーン需要

ブラックリストには、IPフィルタリングを検知します。コンテンツIPのブラックリストは、増加または任意の時点で減少し、したがって、いつでも動的に構成することができます。

オペレータによる変数としてMySQL負荷からブラックリストは、サブFLINKを使用する際にブラックリストは、MySQLがあると仮定される、FLINKジョブが開始されます。

2.問題

私たちは、この変数を取り戻すために仕事を再起動する必要はありません。だから我々は、動的に計算方法ヤード変数を変更できるようにする必要があります。

3.ソリューション

解決する方法を放送しています。動的に更新された設定を行います。

異なる放送と共通のデータ・ストリーム:ストリーム演算子ストリームをデータ放送では、すべてのパーティションを処理することができ、およびストリームのストリームデータは、サブプロセスのパーティションとみなすことができます。したがって、放送ストリームの特性は、動的更新の構成に適し判定する。

0x02の概要

この項では、3つの放送の難しさがあります手順を、機能をカスタマイズする方法を、どのようにアクセス状態に。ここで最初に皆のための概要を示します。

ステップ1.使用して放送

  • MapStateDescriptorを確立
  • DataStream.broadcast方法BroadcastStreamバック放送データストリーム
  • 、トラフィックデータが流れるとDataStream.connect BroadcastStreamメソッド戻りを介して接続されているBroadcastConnectedStream
  • BroadcastConnectedStream.process processBroadcastElement processElementと方法によって実行される処理

2.ユーザ定義ハンドラ

  • KeyedBroadcastProcessFunctionとBroadcastProcessFunction:BroadcastConnectedStream.processは、関数の2つのタイプを受信します
  • 関数の2種類processElement、processBroadcastElement抽象メソッドが定義され、それはKeyedBroadcastProcessFunctionにOnTimerメソッドを定義している、デフォルトの動作は、サブクラスのオーバーライドを可能にし、空であります
  • サービス・データ・フロー・processElementを処理
  • processBroadcastElement処理放送データストリーム

3.ブロードキャスト州

  • 放送状態は常にMapState、すなわちマップ形式で表現されます。これはFLINKは、プリミティブを提供し、最も一般的な状態です。状態、状態管理フレームFLINK、このようValueState、ListState、MapState等により管理された状態を主催しました
  • あなたは、ユーザーを作成する必要がありMapStateDescriptor、対応する状態のハンドルを得るために。これは、状態名、状態に保持された値の型を保存し、ユーザによって指定された機能を含んでいてもよいです
  • 放送状態をチェックポイントになるチェックポイント時間
  • 放送状態はメモリ内だけで、何のRocksDB状態のバックエンドはありません
  • FLINK状態は、各タスクの状態は、タスク全体に広がらない、タスクで彼らだけの役割でそれらを変更することをメモして放送されます
  • それはときに注意するために処理され依存素子に到達するように順次にブロードキャストイベントを受信した下流のタスクは、異なっていてもよいです

0x03の。サンプルコード

1.サンプルコード

私たちは、理想的な例を見つけるために、ソースから直接FLINKで始まります。次の抜粋FLINKのソースコードを直接StatefulJobWBroadcastStateMigrationITCaseは、私は内側に注釈を追加します。

  @Test
  def testRestoreSavepointWithBroadcast(): Unit = {

    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    // 以下两个变量是为了确定广播流发出的数据类型,广播流可以同时发出多种类型的数据
    lazy val firstBroadcastStateDesc = new MapStateDescriptor[Long, Long](
      "broadcast-state-1",
      BasicTypeInfo.LONG_TYPE_INFO.asInstanceOf[TypeInformation[Long]],
      BasicTypeInfo.LONG_TYPE_INFO.asInstanceOf[TypeInformation[Long]])

    lazy val secondBroadcastStateDesc = new MapStateDescriptor[String, String](
      "broadcast-state-2",
      BasicTypeInfo.STRING_TYPE_INFO,
      BasicTypeInfo.STRING_TYPE_INFO)

    env.setStateBackend(new MemoryStateBackend)
    env.enableCheckpointing(500)
    env.setParallelism(4)
    env.setMaxParallelism(4)

    // 数据流,这里数据流和广播流的Source都是同一种CheckpointedSource。数据流这里做了一系列算子操作,比如flatMap
    val stream = env
      .addSource(
        new CheckpointedSource(4)).setMaxParallelism(1).uid("checkpointedSource")
      .keyBy(
        new KeySelector[(Long, Long), Long] {
          override def getKey(value: (Long, Long)): Long = value._1
        }
      )
      .flatMap(new StatefulFlatMapper)
      .keyBy(
        new KeySelector[(Long, Long), Long] {
          override def getKey(value: (Long, Long)): Long = value._1
        }
      )

    // 广播流
    val broadcastStream = env
      .addSource(
        new CheckpointedSource(4)).setMaxParallelism(1).uid("checkpointedBroadcastSource")
      .broadcast(firstBroadcastStateDesc, secondBroadcastStateDesc)

    // 把数据流和广播流结合起来
    stream
      .connect(broadcastStream)
      .process(new VerifyingBroadcastProcessFunction(expectedFirstState, expectedSecondState))
      .addSink(new AccumulatorCountingSink)
  }
}

// 用户自定义的处理函数
class TestBroadcastProcessFunction
  extends KeyedBroadcastProcessFunction
    [Long, (Long, Long), (Long, Long), (Long, Long)] {

  // 重点说明,这里的 firstBroadcastStateDesc,secondBroadcastStateDesc 其实和之前广播流的那两个MapStateDescriptor无关。
      
  // 这里两个MapStateDescriptor是为了存取BroadcastState,这样在 processBroadcastElement和processElement之间就可以传递变量了。我们完全可以定义新的MapStateDescriptor,只要processBroadcastElement和processElement之间认可就行。
      
  // 这里参数 "broadcast-state-1" 是name, flink就是用这个 name 来从Flink运行时系统中存取MapStateDescriptor 
  lazy val firstBroadcastStateDesc = new MapStateDescriptor[Long, Long](
    "broadcast-state-1",
    BasicTypeInfo.LONG_TYPE_INFO.asInstanceOf[TypeInformation[Long]],
    BasicTypeInfo.LONG_TYPE_INFO.asInstanceOf[TypeInformation[Long]])

  val secondBroadcastStateDesc = new MapStateDescriptor[String, String](
    "broadcast-state-2",
    BasicTypeInfo.STRING_TYPE_INFO,
    BasicTypeInfo.STRING_TYPE_INFO)

  override def processElement(
                value: (Long, Long),
                ctx: KeyedBroadcastProcessFunction
                [Long, (Long, Long), (Long, Long), (Long, Long)]#ReadOnlyContext,
                out: Collector[(Long, Long)]): Unit = {

    // 这里Flink源码中是直接把接受到的业务变量直接再次转发出去
    out.collect(value) 
  }

  override def processBroadcastElement(
                value: (Long, Long),
                ctx: KeyedBroadcastProcessFunction
                [Long, (Long, Long), (Long, Long), (Long, Long)]#Context,
                out: Collector[(Long, Long)]): Unit = {
    // 这里是把最新传来的广播变量存储起来,processElement中可以取出再次使用. 具体是通过firstBroadcastStateDesc 的 name 来获取 BroadcastState
    ctx.getBroadcastState(firstBroadcastStateDesc).put(value._1, value._2)
    ctx.getBroadcastState(secondBroadcastStateDesc).put(value._1.toString, value._2.toString)
  }
}

// 广播流和数据流的Source
private class CheckpointedSource(val numElements: Int)
  extends SourceFunction[(Long, Long)] with CheckpointedFunction {

  private var isRunning = true
  private var state: ListState[CustomCaseClass] = _

  // 就是简单的定期发送
  override def run(ctx: SourceFunction.SourceContext[(Long, Long)]) {
    ctx.emitWatermark(new Watermark(0))
    ctx.getCheckpointLock synchronized {
      var i = 0
      while (i < numElements) {
        ctx.collect(i, i)
        i += 1
      }
    }
    // don't emit a final watermark so that we don't trigger the registered event-time
    // timers
    while (isRunning) Thread.sleep(20)
  }
}

2.技術的な問題

MapStateDescriptor

まず、いくつかの概念を説明します:

  • キー付きの状態とオペレータの状態:FLINKは、の二つの基本的な状態が含まれています。
  • 元の状態と管理:キー付きの状態とオペレータの状態とは、2つの形で存在することができます。
  • FLINKはListState、MapState等のようValueStateとして状態管理フレーム、ある状態が管理します。
  • すなわち、元の状態の生の状態、読み書きするために、その内部データ構造の無知を、バイト[]コンテンツを使用するときに自己管理ユーザ特定データ構造の状態によっては、チェックポイントフレームを行います。
  • MapStateは、管理された状態である。状態値がマップです。ユーザput又はputAll要素を追加する方法。

私達の例に戻って、放送はOperatorState可変部分は、管理MapStateの状態を保存することです。GetBroadcastStateの具体的な機能はDefaultOperatorStateBackendで達成されます

ここMapStateDescriptor記述放送状態、より柔軟な使用のMapStateDescriptorを使用する必要があり、我々はそう、それはキー、値と同様の使用であるので、そう個人的に特に学生に他の言語からのScalaのために、より便利である、直接クラスを使用して、その値を感じます。

processBroadcastElement

// 因为主要起到控制作用,所以这个函数的处理相对简单
override def processBroadcastElement(): Unit = {
    // 这里可以把最新传来的广播变量存储起来,processElement中可以取出再次使用,比如
    ctx.getBroadcastState(firstBroadcastStateDesc).put(value._1, value._2)
}

processElement

// 这个函数需要和processBroadcastElement配合起来使用
override def processElement(): Unit = {
    // 可以取出processBroadcastElement之前存储的广播变量,然后用此来处理业务变量,比如
   val secondBroadcastStateDesc = new MapStateDescriptor[String, String](
    "broadcast-state-2",
    BasicTypeInfo.STRING_TYPE_INFO,
    BasicTypeInfo.STRING_TYPE_INFO)  

    var actualSecondState = Map[String, String]()
    for (entry <- ctx.getBroadcastState(secondBroadcastStateDesc).immutableEntries()) {
      val v = secondExpectedBroadcastState.get(entry.getKey).get
      actualSecondState += (entry.getKey -> entry.getValue)
    }  

   // 甚至这里只要和processBroadcastElement一起关联好,可以存储任意类型的变量。不必须要和广播变量的类型一致。重点是声明新的对应的MapStateDescriptor
   // MapStateDescriptor继承了StateDescriptor,其中state为MapState类型,value为Map类型
}

組み合わせで

いくつかの制限があるので、これだけの話に皆のためのインターネットから例を検索するには、次の。

// 模式始终存储在MapState中,并将null作为键。broadcast state始终表示为MapState,这是Flink提供的最通用的状态原语。
MapStateDescriptor<Void, Pattern> bcStateDescriptor = 
  new MapStateDescriptor<>("patterns", Types.VOID, Types.POJO(Pattern.class));

// 能看到的是,在处理广播变量时候,存储广播变量到BroadcastState
 public void processBroadcastElement(Pattern pattern, Context ctx, 
     Collector<Tuple2<Long, Pattern>> out) throws Exception {
   // store the new pattern by updating the broadcast state
   BroadcastState<Void, Pattern> bcState = ctx.getBroadcastState(patternDesc);
   // storing in MapState with null as VOID default value
   bcState.put(null, pattern);
 }

// 能看到的是,在处理业务变量时候,从BroadcastState取出广播变量,存取时候实际都是用"patterns"这个name字符串来作为key。
  public void processElement(Action action, ReadOnlyContext ctx, 
     Collector<Tuple2<Long, Pattern>> out) throws Exception {
   // get current pattern from broadcast state
   Pattern pattern = ctx.getBroadcastState(this.patternDesc)
     // access MapState with null as VOID default value
     .get(null);
   // get previous action of current user from keyed state
   String prevAction = prevActionState.value();
   if (pattern != null && prevAction != null) {
     // user had an action before, check if pattern matches
     if (pattern.firstAction.equals(prevAction) && 
         pattern.secondAction.equals(action.action)) {
       // MATCH
       out.collect(new Tuple2<>(ctx.getCurrentKey(), pattern));
     }
   }
   // update keyed state and remember action for next pattern evaluation
   prevActionState.update(action.action);
 }

1.論理フロー放送

 * The life cycle of the Broadcast:
 * {@code
 *  -- 初始化逻辑 -> 用一个BroadcastConnectedStream把数据流和广播流结合起来进行拓扑转换
 *        |   
 *        +---->  businessStream = DataStream.filter.map....
 *        |       // 处理业务逻辑的数据流,businessStream 是普通DataStream  
 *        +---->  broadcastStream = DataStream.broadcast(broadcastStateDesc)
 *        |       // 处理配置逻辑的广播数据流,broadcastStream是BroadcastStream类型
 *        +---->  businessStream.connect(broadcastStream)
 *        |                     .process(new processFunction(broadcastStateDesc))
 *        |       // 把业务流,广播流 结合起来,生成一个BroadcastConnectedStream,然后进行 process
 *        +----------> process @ BroadcastConnectedStream   
 *        |                TwoInputStreamOperator<IN1, IN2, OUT> operator =
 *        |                new CoBroadcastWithNonKeyedOperator<>(clean(function),
 *        |                broadcastStateDescriptors);
 *        |                return transform(outTypeInfo, operator);  
 *        |       // 生成一个类型是TwoInputStreamOperator 的 operator,进行 transform
 *        +----------------> transform @ BroadcastConnectedStream  
 *        |                      transform = new TwoInputTransformation<>(
 *        |       			  	       inputStream1.getTransformation(), // 业务流
 *        |       			  	       inputStream2.getTransformation(), // 广播流
 *        |       			  	       ifunctionName, // 用户的UDF
 *        |       			  	       operator, // 算子 CoBroadcastWithNonKeyedOperator
 *        |       			  	       outTypeInfo);  // 输出类型
 *        |       	      		   returnStream = new SingleOutputStreamOperator(transform);
 *        |       			         getExecutionEnvironment().addOperator(transform)
 *        |       // 将业务流,广播流与拓扑联合起来形成一个转换,加到 Env 中,这就完成了拓扑转换 
 *        |       // 最后返回结果是一个SingleOutputStreamOperator。
 * }


 *  数据结构:
 *  -- BroadcastStream. 
 *  就是简单封装一个DataStream,然后记录这个广播流对应的StateDescriptors  
 public class BroadcastStream<T> {  
	private final StreamExecutionEnvironment environment;
	private final DataStream<T> inputStream;
	private final List<MapStateDescriptor<?, ?>> broadcastStateDescriptors;   
 }
   
 *  数据结构:
 *  -- BroadcastConnectedStream. 
 *  把业务流,广播流 结合起来,然后会生成算子和拓扑
public class BroadcastConnectedStream<IN1, IN2> {
	private final StreamExecutionEnvironment environment;
	private final DataStream<IN1> inputStream1;
	private final BroadcastStream<IN2> inputStream2;
	private final List<MapStateDescriptor<?, ?>> broadcastStateDescriptors;
}  

*  真实计算:
*  -- CoBroadcastWithNonKeyedOperator -> 真正对BroadcastProcessFunction的执行,是在这里完成的
public class CoBroadcastWithNonKeyedOperator<IN1, IN2, OUT>
		extends AbstractUdfStreamOperator<OUT, BroadcastProcessFunction<IN1, IN2, OUT>>
		implements TwoInputStreamOperator<IN1, IN2, OUT> {
  
  private final List<MapStateDescriptor<?, ?>> broadcastStateDescriptors;
	private transient TimestampedCollector<OUT> collector;
	private transient Map<MapStateDescriptor<?, ?>, BroadcastState<?, ?>> broadcastStates;
	private transient ReadWriteContextImpl rwContext;
	private transient ReadOnlyContextImpl rContext;
  
	@Override
	public void processElement1(StreamRecord<IN1> element) throws Exception {
		collector.setTimestamp(element);
		rContext.setElement(element);
    // 当上游有最新业务数据来的时候,调用用户自定义的processElement
    // 在这可以把之前存储的广播配置信息取出,然后对业务数据流进行处理    
		userFunction.processElement(element.getValue(), rContext, collector);
		rContext.setElement(null);
	}

	@Override
	public void processElement2(StreamRecord<IN2> element) throws Exception {
		collector.setTimestamp(element);
		rwContext.setElement(element);
    // 当上游有数据来的时候,调用用户自定义的processBroadcastElement
    // 在这可以把最新传送的广播配置信息存起来  
		userFunction.processBroadcastElement(element.getValue(), rwContext, collector);
		rwContext.setElement(null);
	}  
}

2.でDataStreamキー機能

// 就是connect,broadcast,分别生成对应的数据流
public class DataStream<T> {
  protected final StreamExecutionEnvironment environment;
  protected final Transformation<T> transformation;

	@PublicEvolving
	public <R> BroadcastConnectedStream<T, R> connect(BroadcastStream<R> broadcastStream) {
		return new BroadcastConnectedStream<>(
				environment,
				this,
				Preconditions.checkNotNull(broadcastStream),
				broadcastStream.getBroadcastStateDescriptor());
	}
		
	@PublicEvolving
	public BroadcastStream<T> broadcast(final MapStateDescriptor<?, ?>... broadcastStateDescriptors) {
		final DataStream<T> broadcastStream = setConnectionType(new BroadcastPartitioner<>());
		return new BroadcastStream<>(environment, broadcastStream, broadcastStateDescriptors);
	}
}

3.キーデータ構造MapStateDescriptor

主に、メタデータのさまざまな情報を宣言するために使用されます。フォローアップ見ることができ、システムは、店舗に、すなわち、最初のパラメータ、MapStateDescriptor名によるものである/ MapStateDescriptor状態に対応する取得。

public class MapStateDescriptor<UK, UV> extends StateDescriptor<MapState<UK, UV>, Map<UK, UV>> {
	/**
	 * Create a new {@code MapStateDescriptor} with the given name and the given type serializers.
	 *
	 * @param name The name of the {@code MapStateDescriptor}.
	 * @param keySerializer The type serializer for the keys in the state.
	 * @param valueSerializer The type serializer for the values in the state.
	 */  
	public MapStateDescriptor(String name, TypeSerializer<UK> keySerializer, TypeSerializer<UV> valueSerializer) {
		super(name, new MapSerializer<>(keySerializer, valueSerializer), null);
	}

	/**
	 * Create a new {@code MapStateDescriptor} with the given name and the given type information.
	 *
	 * @param name The name of the {@code MapStateDescriptor}.
	 * @param keyTypeInfo The type information for the keys in the state.
	 * @param valueTypeInfo The type information for the values in the state.
	 */
	public MapStateDescriptor(String name, TypeInformation<UK> keyTypeInfo, TypeInformation<UV> valueTypeInfo) {
		super(name, new MapTypeInfo<>(keyTypeInfo, valueTypeInfo), null);
	}

	/**
	 * Create a new {@code MapStateDescriptor} with the given name and the given type information.
	 *
	 * <p>If this constructor fails (because it is not possible to describe the type via a class),
	 * consider using the {@link #MapStateDescriptor(String, TypeInformation, TypeInformation)} constructor.
	 *
	 * @param name The name of the {@code MapStateDescriptor}.
	 * @param keyClass The class of the type of keys in the state.
	 * @param valueClass The class of the type of values in the state.
	 */
	public MapStateDescriptor(String name, Class<UK> keyClass, Class<UV> valueClass) {
		super(name, new MapTypeInfo<>(keyClass, valueClass), null);
	}
}

4.アクセス状況

processElement processBroadcastElement間の状態の転送および、MapStateDescriptorキーの名前によって、FLINKを格納しますそれは同様の呼び出しに続きますctx.getBroadcastState(firstBroadcastStateDesc).put(value._1, value._2)したがって、我々の次の必要性はFLINKの下に国家の概念を導入します。

チェックポイント対州

まず、二つの概念を区別し、状態は、一般に特定のタスク/オペレータの状態を指します。チェックポイントは、すべてのタスク/オペレータの状態を含む時間における特定の瞬間の状態のグローバル・スナップショットで、FLINKジョブを示します。定期的にチェックポイントを実行して、耐障害性と回復を達成するためにFLINK。

キー付きの状態とオペレータの状態:FLINKは、の二つの基本的な状態が含まれています。

キー付きの状態

名前が示すように、それはKeyedStreamの状態に基づいています。状態は、おそらく状態に対応し、特定のキー、及びKeyedStreamフロー上の各キーと結合することです。

オペレータ州

異なるとキー付きの状態では、オペレータの状態は、全体のオペレータが一つだけの状態を持って、バインドされたインスタンスの特定のオペレータと同時に。比較では、オペレータに、キー付き状態のように対応する複数のキーの数が存在してもよいです。

例えば、カフカコネクタFLINKは、オペレータの状態を使用することです。これは、消費者の話題(パーティション、オフセット)マッピングのすべてのインスタンスを保存するために、各コネクター・インスタンスのためになります。

FLINKは元の状態および状態(生と管理された状態)をホスティング

これは別の次元です。

キー付き状態オペレータ州 2つの形態でそれぞれ存在します管理およびRAW、すなわち、元の状態とホスト状態。

そのようなハッシュ・テーブルまたはRocksDBの内部のような状態を、実行状態管理FLINKフレームによって管理されます。例えば、「ValueState」、「ListState」というように。FLINKは、これらの状態がチェックポイントをエンコードし、書き込みますランタイム。

例えば、現在のキー入力データの状態に作用アクセスインターフェースの種類を、提供するインターフェイスのキー付き状態ステータスを管理しました。言い換えれば、これらの状態のみをすることができますKeyedStream使用し、することができますstream.keyBy(...)取得KeyedStreamそして、我々は達成することができCheckpointedFunctionたりListCheckpointed、管理オペレータの状態のインタフェースを使用します。

生の状態、すなわち元の状態、自己管理オペレータ独自のデータ構造に格納されたユーザ固有のデータ構造の状態によって。チェックポイントの時間、具体的な内容FLINK、チェックポイントに書き込まれたバイトの配列のみを知りません。

通常、ユーザー定義の演算子を実装する場合、それは元の状態に使用されるでDataStream、上の状態でホストされている方におすすめです。

私達の例に戻って、放送はOperatorState可変部分は、管理MapStateの状態を保存することです。特定getBroadcastState機能は実装でDefaultOperatorStateBackendあります

StateDescriptor

あなたは、作成する必要がありStateDescriptor、対応する状態へのハンドルを取得するには、。これは、状態名(あなたが複数の状態を作成することができますし、あなたがそれらを参照できるように、彼らは固有の名前を持つ必要があります)、状態が保持している値の型を保存し、例えば、ユーザーが指定した機能が含まれていてもよいですReduceFunctionステータスの種類に応じて、作成することができValueStateDescriptorListStateDescriptorReducingStateDescriptorFoldingStateDescriptorまたはMapStateDescriptor

状態によってRuntimeContext訪問、これだけ豊富な機能は使用しています。

OperatorStateBackEnd

。OperatorStateBackEnd鍵管理OperatorState現在、唯一の実装:DefaultOperatorStateBackend。

DefaultOperatorStateBackend

DefaultOperatorStateBackendの状態は、ストアへのマップ方法です。PartitionableListState(所属ListState)を構築しました。OperatorStateは、メモリに格納されています。

public class DefaultOperatorStateBackend implements OperatorStateBackend {
  
	/**
	 * Map for all registered operator states. Maps state name -> state
	 */
	private final Map<String, PartitionableListState<?>> registeredOperatorStates;

	/**
	 * Map for all registered operator broadcast states. Maps state name -> state
	 */
	private final Map<String, BackendWritableBroadcastState<?, ?>> registeredBroadcastStates;  
  
  /**
	 * Cache of already accessed states.
	 *
	 * <p>In contrast to {@link #registeredOperatorStates} which may be repopulated
	 * with restored state, this map is always empty at the beginning.
	 *
	 * <p>TODO this map should be moved to a base class once we have proper hierarchy for the operator state backends.
	 */
	private final Map<String, PartitionableListState<?>> accessedStatesByName;

	private final Map<String, BackendWritableBroadcastState<?, ?>> accessedBroadcastStatesByName;  // 这里用来缓存广播变量
  
  // 这里就是前文中所说的,存取广播变量的API
	public <K, V> BroadcastState<K, V> getBroadcastState(final MapStateDescriptor<K, V> stateDescriptor) throws StateMigrationException {

		String name = Preconditions.checkNotNull(stateDescriptor.getName());

    // 如果之前有,就取出来
		BackendWritableBroadcastState<K, V> previous =
			(BackendWritableBroadcastState<K, V>) accessedBroadcastStatesByName.get(
      name);

		if (previous != null) {
			return previous;
		}

		stateDescriptor.initializeSerializerUnlessSet(getExecutionConfig());
		TypeSerializer<K> broadcastStateKeySerializer = Preconditions.checkNotNull(stateDescriptor.getKeySerializer());
		TypeSerializer<V> broadcastStateValueSerializer = Preconditions.checkNotNull(stateDescriptor.getValueSerializer());

		BackendWritableBroadcastState<K, V> broadcastState =
			(BackendWritableBroadcastState<K, V>) registeredBroadcastStates.get(name);

		if (broadcastState == null) {
			broadcastState = new HeapBroadcastState<>(
					new RegisteredBroadcastStateBackendMetaInfo<>(
							name,
							OperatorStateHandle.Mode.BROADCAST,
							broadcastStateKeySerializer,
							broadcastStateValueSerializer));
			registeredBroadcastStates.put(name, broadcastState);
		} else {
			// has restored state; check compatibility of new state access

			RegisteredBroadcastStateBackendMetaInfo<K, V> restoredBroadcastStateMetaInfo = broadcastState.getStateMetaInfo();

			// check whether new serializers are incompatible
			TypeSerializerSchemaCompatibility<K> keyCompatibility =
				restoredBroadcastStateMetaInfo.updateKeySerializer(broadcastStateKeySerializer);

			TypeSerializerSchemaCompatibility<V> valueCompatibility =
				restoredBroadcastStateMetaInfo.updateValueSerializer(broadcastStateValueSerializer);

			broadcastState.setStateMetaInfo(restoredBroadcastStateMetaInfo);
		}

		accessedBroadcastStatesByName.put(name, broadcastState); // 如果之前没有,就存入
		return broadcastState;
	}  
}

0x05の参考

FLINK原理と実装:状態管理にFLINK詳細https://yq.aliyun.com/articles/225623

動的更新を達成するためにFLINK放送構成https://www.jianshu.com/p/c8c99f613f10

FLINK放送州立実践ガイドhttps://blog.csdn.net/u010942041/article/details/93901918

放送の話FLINK州https://www.jianshu.com/p/d6576ae67eae

州での作業https://ci.apache.org/projects/flink/flink-docs-stable/zh/dev/stream/state/state.html

おすすめ

転載: www.cnblogs.com/rossiXYZ/p/12594315.html