目次
Kafka消費フォールトトレランス(チェックポイントメカニズム)
コネクタの依存関係をインポートする
Flink自体はkafkaにリンクするためのインターフェースを提供していません。使用する前に、関連する依存関係をインポートする必要があります。flink 1.7以降、flink-connector-kafkaは自動的に最新バージョンのKafkaに適応しますが、0.11、0.10、0.9、0.8などのより低いバージョンのKafkaを使用する場合は、対応するKafkaコネクタを使用する必要があります。
Mavenの依存関係 | flinkバージョン | コンシューマーとプロデューサーのクラス名 | カフカバージョン | 備考 |
---|---|---|---|---|
flink-connector-kafka-0.8_2.11 | 1.0.0 | FlinkKafkaConsumer08 FlinkKafkaProducer08 |
0.8.x | KafkaのSimpleConsumer APIを内部で使用します。オフセットはFlinkによってZKに送信されます。 |
flink-connector-kafka-0.9_2.11 | 1.0.0 | FlinkKafkaConsumer09 FlinkKafkaProducer09 |
0.9.x | 新しいConsumer API Kafkaを使用します。 |
flink-connector-kafka-0.10_2.11 | 1.2.0 | FlinkKafkaConsumer010 FlinkKafkaProducer010 |
0.10.x | コネクターは、本番用および使用用のタイムスタンプ付きのKafkaメッセージをサポートします。 |
flink-connector-kafka-0.11_2.11 | 1.4.0 | FlinkKafkaConsumer011 FlinkKafkaProducer011 |
0.11.x | 0.11.x以降、KafkaはScala 2.10をサポートしていません。コネクタはKafkaトランザクションメッセージングをサポートし、プロデューサーに正確な1回限りのセマンティクスを提供します。 |
flink-connector-kafka_2.11 | 1.7.0 | FlinkKafka コンシューマーFlinkKafkaプロデューサー |
> = 1.0.0 | このユニバーサルKafkaコネクタは、最新バージョンのKafkaに適合します。Flinkが使用するクライアントバージョンは、リリース間で異なる場合があります。Flink 1.9バージョンから、Kafka 2.2.0クライアントを使用しています。Kafkaクライアントは、バージョン0.10.0以降と下位互換性があります。ただし、Kafka 0.11.xおよび0.10.xバージョンの場合は、それぞれ専用のflink-connector-kafka-0.11_2.11およびflink-connector-kafka-0.10_2.11を使用することをお勧めします。 |
信頼する:
<!-- https://mvnrepository.com/artifact/org.apache.flink/flink-connector-kafka-0.11 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.11</artifactId>
<version>1.10.0</version>
</dependency>
暗黙的な変換をインポートする
Scalaは暗黙的な変換をインポートする必要があります。そうしないと、エラーが報告されます。次のコード例ではインポートされません。
import org.apache.flink.api.scala._
Flinkのカフカ消費者
Flinkのカフカコンシューマクラス名は FlinkKafkaConsumer08
(08はカフカバージョンで、対応するflinkコンシューマクラス名Kafka 0.9.0.xなどFlinkKafkaConsumer09
)です。
flinkコンシューマーを作成するには、3つのパラメーターを渡す必要があります。
- topic:トピック名、または複数のトピック名を含むリスト。
- シリアライザ:メッセージのシリアル化に使用されます。
- プロパティオブジェクト:bootstrap.servers、コンシューマグループ、0.8以前のバージョンのKafkaなど、さまざまな構成が含まれています。オフセットを保存するにはzkアドレスも必要です。
といった:
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
// only required for Kafka 0.8
properties.setProperty("zookeeper.connect", "localhost:2181")
properties.setProperty("group.id", "test")
stream = env
.addSource(new FlinkKafkaConsumer08[String]("topic", new SimpleStringSchema(), properties))
.print()
デシリアライザ
flinkがKafkaを使用する場合、KafkaのバイナリデータをJava / Scalaのオブジェクトに変換する方法を知る必要があります。Flinkには、上記の例のSimpleStringSchemaに加えて、さまざまなデシリアライザーが付属しています。
TypeInformationSerializationSchema(或
TypeInformationKeyValueSerializationSchema )
:flinkのTypeInformationに基づいてスキーマを作成します。データがflink間でのみ流れる場合、このメソッドを使用でき、パフォーマンスは他の逆シリアル化メソッドよりも高くなります。
JsonDeserializationSchema (或
JSONKeyValueDeserializationSchema )
:kafkaのjsonデータをObjectNodeオブジェクトに変換できます。これを使用objectNode.get("field").as(Int/String/...)()
して、指定されたフィールドにアクセスできます。括弧内でデシリアライザーを使用している場合objectNode,
、jsonのすべてのフィールドが含まれているだけでなく、トピック、パーティション、オフセットなどのkafkaのメタデータ情報も含まれているk / vタイプを取得します。
AvroDeserializationSchema
:静的スキーマを使用してAvro形式でシリアル化されたデータを読み取るために使用されます。スキーマ情報は、Avroによって生成されたクラス(AvroDeserializationSchema.forSpecific(...)など)から推測できます;スキーマ情報を手動で指定することもできます(AvroDeserializationSchema.forGeneric(...)によって生成されたGenericRecordsクラスを使用します)。Avroシリアル化を使用するには、flink-avroやflink-avro-confluent-registryなどの対応する依存関係をインポートする必要があります。
カスタムデシリアライザー
Flinkは 、デシリアライザをカスタマイズDeserializationSchema
するためにFlink のT deserialize(byte[] message)
メソッドを書き換えることができるインターフェースを提供します。deserializeはすべてのkafkaメッセージを処理し、カスタムタイプのデータを返します。
例としてKeyedDeserializationSchemaを取り上げ、deserializeメソッドを書き換えて、トピック、キー、値を含むトリプルを返します。
public class KafkaDeserializationTopicSchema implements KeyedDeserializationSchema<Tuple3<String,String,String>> {
public KafkaDeserializationTopicSchema(){
}
@Override
public Tuple3 deserialize(byte[] keyByte, byte[] message, String topic, int partition, long offset) throws IOException {
String key = null;
String value = null;
if (keyByte != null) {
key = new String(keyByte, StandardCharsets.UTF_8);
}
if (message != null) {
value = new String(message,StandardCharsets.UTF_8);
}
return new Tuple3(topic,key, value);
}
@Override
public boolean isEndOfStream(Tuple3 o) {
return false;
}
@Override
public TypeInformation getProducedType() {
return TypeInformation.of(new TypeHint<Tuple3<String,String,String>>(){});
}
消費の開始点を設定する
FlinkはKafkaの消費の開始点を設定できます。といった:
val env = StreamExecutionEnvironment.getExecutionEnvironment()
val myConsumer = new FlinkKafkaConsumer08[String](...)
myConsumer.setStartFromEarliest() // 从最早的offset开始消费
myConsumer.setStartFromLatest() // 从最迟的offset开始消费
myConsumer.setStartFromTimestamp(...) // 从指定的时间开始消费
myConsumer.setStartFromGroupOffsets() // 从当前组消费到的offset开始消费(默认的消费策略)
val stream = env.addSource(myConsumer)
setStartFromGroupOffsets
:消費者グループからKafkaに送信されたオフセットから消費を開始します。Kafkaバージョン0.8はzookeeperに保存されます。0.8以降、Kafkaにはオフセットの保存に関するトピックがあります。オフセットが見つからない場合、消費はauto.offset.resetで設定されたパラメーターから始まります。
setStartFromEarliest()
/ setStartFromLatest():
最も早い/最も遅いオフセットから消費を開始します。このモードが使用される場合、送信されたオフセットは無視され、送信されたオフセットから消費されません。
setStartFromTimestamp(long):指定されたタイムスタンプから消費を開始します。各パーティションでは、このタイムスタンプ以上のタイムスタンプを持つデータが消費されます。パーティション内の最新データのタイムスタンプが指定されたタイムスタンプより小さい場合、消費は最新のタイムスタンプから開始されます。このモードが使用される場合、送信されたオフセットは無視され、消費は送信されたオフセットから開始されません。
オフセット開始点を指定してください
特定のオフセットから消費を開始するように各パーティションを手動で指定することもできます。たとえば、「myTopic」は消費のトピック、0 1 2はトピックのパーティション番号、23 31 43は次の消費オフセットであるオフセットです。パーティションが指定されていない場合、消費はコンシューマグループによって消費されたオフセットから開始されsetStartFromGroupOffsets
ます。つまり、モードにフォールバックします。
val specificStartOffsets = new java.util.HashMap[KafkaTopicPartition, java.lang.Long]()
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 0), 23L)
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 1), 31L)
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 2), 43L)
myConsumer.setStartFromSpecificOffsets(specificStartOffsets)
注:ジョブが障害から自動的に復元されるか、チェックポイントを使用して手動で復元されると、消費は保存された状態のオフセットから続行され、これらの設定は再び使用されません。
Kafka消費フォールトトレランス(チェックポイントメカニズム)
チェックポイントメカニズムがオンになった後、flinkはKafkaの使用時に定期的にKafkaのオフセットと作業ステータス(計算結果を含む)を保存し、データの整合性を確保します。タスクでエラーが発生した場合、flinkはチェックポイントから計算状態を復元し、保存されたオフセットからデータの消費を再開します。
したがって、チェックポイントを保存する間隔によって、タスクが失敗したときに失われるデータの量が決まります。
チェックポイントは、addsinkを呼び出す前に、コンテキストオブジェクトのenableCheckpointingメソッドを呼び出す必要があります。パラメーターは、チェックポイントを保存する時間間隔(ミリ秒)です。コードは以下のように表示されます:
val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.enableCheckpointing(5000) // checkpoint every 5000 msecs
ゾーン認識
Flinkの消費Kafkaは、Kafkaパーティションの変更の動的な認識をサポートしています。Kafkaが新しいパーティションを作成すると、flinkは新しいパーティションを検出して、それらを1回だけ消費できます。パーティションメタデータの最初の取得後に検出されたすべてのパーティション(つまり、ジョブの実行開始時に検出されたパーティション)は、最も古いオフセットから消費されます。
デフォルトでは、パーティション認識は無効になっています。プロパティでflink.partition-discovery.interval-millisのパラメーターを設定することで、パーティション認識を有効にできます。このパラメーターは、パーティションの変更を確認する間隔をミリ秒単位で示す負でない値です。 。
Flinkは正規表現を使用して、次のようなトピック名に基づいてトピックを照合することもできます。
val env = StreamExecutionEnvironment.getExecutionEnvironment()
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "test")
val myConsumer = new FlinkKafkaConsumer08[String](
java.util.regex.Pattern.compile("test-topic-[0-9]"),
new SimpleStringSchema,
properties)
val stream = env.addSource(myConsumer)
上記の例では、flinkは「test-topic-」で始まり、数字で終わるすべてのトピックを消費します。
オフセットコミット構成
Kafkaは、flinkによって使用されたオフセットをkafkaの組み込みトピックに送信します(kafkaのバージョン0.8はzookeeperに保存されます)が、flinkはこれらのオフセットをフォールトトレランスメカニズムとして使用しません
、Kafkaによって保存されたオフセットは、Kafkaが消費ステータスを監視するためにのみ使用されます。
チェックポイントが有効かどうかに応じて、オフセットを送信する方法はいくつかあります。
チェックポイントを有効にする:チェックポイントが有効な場合、flinkはまずオフセットと状態をチェックポイントに保存し、次にオフセットをKafkaに送信します。これにより、kafkaおよびチェックポイントに保存されたオフセットが一貫していることが保証されます。setCommitOffsetsOnCheckpoints(boolean)を使用して、オフセットをKafkaに送信するかどうかを設定できます。デフォルトはtrueです。setCommitOffsetsOnCheckpointsが呼び出された場合、setCommitOffsetsOnCheckpointsによって送信されたパラメータは、プロパティで構成されたパラメータをオーバーライドします。
チェックポイントを無効にする:チェックポイントが無効になっている場合、flinkは消費のためにKafkaに送信されたオフセットに依存します。enable.auto.commit
これは、プロパティ(これはKafkaのバージョン0.8の場合auto.commit.enable
)または auto.commit.interval.ms
オフセットと送信間隔を自動的に送信するかどうかを設定するために使用できます。
タイムスタンプを抽出して透かしを生成する
Kafkaデータにはイベントのタイムスタンプが含まれる場合があり、タイムスタンプと透かしは別の記事で詳細に説明されているため、ここでは繰り返しません。イベントのタイムスタンプを使用する必要がない場合は、このセクションをスキップできます。
ウォーターマークジェネレーターを設定してタイムスタンプスタンプを登録するには、kafkaコンシューマーオブジェクトのassignTimestampsAndWatermarksメソッドを呼び出して、次のようなカスタムウォーターマークジェネレーターを渡します。
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
// only required for Kafka 0.8
properties.setProperty("zookeeper.connect", "localhost:2181")
properties.setProperty("group.id", "test")
val myConsumer = new FlinkKafkaConsumer08[String]("topic", new SimpleStringSchema(), properties)
myConsumer.assignTimestampsAndWatermarks(new CustomWatermarkEmitter())
stream = env
.addSource(myConsumer)
.print()
透かしと透かしジェネレータ(アロケータ)をカスタマイズする方法、参照:https : //blog.csdn.net/x950913/article/details/106246807
Flinkのカフカプロデューサー
flinkのkafkaプロデューサークラス名はFlinkKafkaProducer011
(Kafka 0.10.0.xバージョンのFlinkKafkaProducer010;
場合、kafkaバージョンが1.0.0より高い場合はyes FlinkKafkaProducer
)です。プロデューサーオブジェクトを使用して、1つ以上のトピックにデータを書き込むことができます。
コード例:
val stream: DataStream[String] = ...
val myProducer = new FlinkKafkaProducer011[String](
"localhost:9092", // broker list
"my-topic", // target topic
new SimpleStringSchema) // serialization schema
// versions 0.10+ allow attaching the records' event timestamp when writing them to Kafka;
// this method is not available for earlier Kafka versions
myProducer.setWriteTimestampToKafka(true)
stream.addSink(myProducer)
シリアライザ
カフカ消費者のデシリアライザを参照してください。
カフカプロデューサーパーティショナー
パーティショナーが設定されていない場合、flinkは独自のFlinkFixedPartitioner
パーティショニングを使用しますデフォルトでは、各並列サブタスクはパーティションを生成します。つまり、パーティションの数は並列度と等しくなります。
パーティショナーはFlinkKafkaPartitionerクラスを継承することでカスタマイズできます。すべてのバージョンでカスタムパーティショナーがサポートされています。
注:パーティショナーは各flinkノードに送信されるため、シリアライズ可能でなければなりません。さらに、パーティショナーはチェックポイントに保存されないため、パーティショナーに状態を保存しないでください。保存しないと、タスクが失敗した後、パーティショナーの状態が失われます。
パーティショナーを使用することもできず、シリアライザーを使用してデータの各部分のパーティションを指定できます。この場合、パーティショナーを設定するときに、パーティショナーとしてnullを使用する必要があります(nullを指定する必要があります。これは、パーティショナーが指定されていない場合、デフォルトのFlinkFixedPartitioner
パーティショナーが使用されるためです)。
Kafkaプロデューサーのフォールトトレランスメカニズム
Kafka 0.8バージョン
Kafkaバージョン0.8は、正確な1回限りのフォールトトレランスをサポートしていません。
Kafka 0.9および0.10バージョン
チェックポイントがオンになった後、Kafkaバージョン0.9および0.10は少なくとも1つの消費をサポートします。
チェックポイントを開くことに加えて、setLogFailuresOnly(boolean)
およびsetFlushOnCheckpoint(boolean)
メソッドを使用して他のパラメーターを構成する必要があります。
- setLogFailuresOnlyの
:
デフォルトはfalseです。trueに設定した後、例外が発生すると、エラー情報のみが記録され、例外はスローされません。例外が発生した場合も、データはKafkaに正常に送信されたと見なされます。したがって、少なくとも1つの送信を保証する場合は、パラメーターをfalseに設定します。 - setFlushOnCheckpoint:デフォルトはtrueです。有効にすると、flinkがチェックポイントを保存するときに、オフセットと状態を保存する前に、kafkaが確認応答を返すのを待ちます。これにより、オフセットと状態がチェックポイントに書き込まれる前に、すべてのデータがKafkaに送信されます。少なくとも1つの送信を保証する場合は、trueに設定する必要があります。
Kafkaバージョン0.9および0.10の少なくとも1つの送信を確実にしたい場合は、チェックポイントを有効にし、setLogFailuresOnlyがfalseで、setFlushOnCheckpointがtrueであることを確認する必要があります。
注:デフォルトの送信の再試行回数は0であるため、setLogFailuresOnlyがfalseの場合、エラーが発生すると、データの送信はすぐに失敗します。デフォルト値の0は、重複データが生成されないようにするためのものです。本番環境では、再試行回数を増やすことをお勧めします。
Kafka 0.11以降
チェックポイントが使用されているFlinkKafkaProducer011
場合、(Kafkaのバージョンが1.0.0より高い場合は、はいFlinkKafkaProducer
)正確で1回限りであることが保証されます。
チェックポイントを開いた後、3つの送信モードを選択できます。FlinkKafkaProducer011
セマンティックのパラメータを設定することにより:
- Semantic.NONE:Flinkは正確性を保証しません。データが失われたり、繰り返し送信される可能性があります。
- Semantic.AT_LEAST_ONCE:デフォルトの設定です。少なくとも1回は送信されることが保証されています。データは失われませんが、繰り返される可能性があります。これは、Kafka 0.9および0.10でsetFlushOnCheckpointがtrueであるのと同じです。
- Semantic.EXACTLY_ONCE:正確な1回限りの送信を保証します。kafkaのトランザクションメカニズムを使用して達成します(flinkの2フェーズコミットと連携します。別の記事でチェックポイントメカニズムと2フェーズコミットメカニズムの詳細を説明します)。公式のヒントは、Kafkaへの書き込み時にトランザクションメカニズムが使用されている場合、変更
isolation.level
が必要な設定(read_committed
またはread_uncommitted 后者为默认值,应该修改为前者
)は 以下の「注意」に記載されています。
注:Semantic.EXACTLY_ONCEモードでは、障害が発生した後、送信を続行するにはチェックポイントから状態を復元する必要があります。リカバリー時間(またはflink障害時間)がKafkaトランザクションのタイムアウト時間よりも長い場合、データは失われます。つまり、最初のコミット後、flinkがハングし、flinkが復元されると、2フェーズコミットが実行されますが、トランザクションがタイムアウトになり、今回送信されたデータは失われます。これに基づいて、Kafkaのトランザクションタイムアウトを適切に調整できます。
Kafkaのデフォルトのタイムアウトは、transaction.max.timeout.msによって設定されます。これはデフォルトで15分です。2フェーズコミット間隔が15分を超えると、トランザクションは失敗します。FlinkKafkaProducer011は、デフォルト値を1時間に変更しました。したがって、正確な1回限りのセマンティクスを使用する場合は、この値を適切に増やす必要があります。
Kafkaのコンシューマーがread_committedモードを使用する場合、トランザクションが完了していないトランザクションがコミットされる前に、トランザクション後のすべてのデータはコンシューマーによって読み取られません。たとえば、2つのトランザクションタイムラインが次のとおりであるとします。
- トランザクションAを送信します。
- トランザクションBを送信します。
- トランザクションBを送信します。
トランザクションBはトランザクションAの前にコミットされますが、トランザクションAはまだコミットされていないため、read_uncommitted
モードが使用されない限り、コンシューマはトランザクションBによって送信されたデータを読み取ることができません。
再度注意してください: Semantic.EXACTLY_ONCE
モードでは、各 FlinkKafkaProducer011
インスタンスは固定サイズのカフカプロデューサープールを使用し、各カフカプロデューサーにはチェックポイントがあります。現在の同時チェックポイント数がプールのサイズより大きい場合、flinkはエラーを報告し、アプリケーションを失敗させます。したがって、このプールのサイズを適切に増やすことをお勧めします。
最後に、上記のとおり、トランザクションが遅延し、コンシューマがread_committedモードの場合、トランザクション後に送信されたデータを読み取ることができません。次に、flinkプログラムがトランザクションを送信するが、コミットされず、最初のチェックポイントが生成される前に、flinkがハングするような状況が発生します。flinkが再起動すると、チェックポイントに以前の情報がないため、送信できません。このトランザクションは、このコミットされていないトランザクションの存在を認識せず、他のデータを送信し続けます。その後、コンシューマーはデータを消費できなくなり、データもロールバックされる可能性があります(推測、検証される)。チェックポイントを生成する前に、flinkプログラムがハングアップしないことを確認してください。
このパラメータFlinkKafkaProducer011.SAFE_SCALE_DOWN_FACTORはこのメカニズムに関連しているようです。ここに残して、時間があるときによく見てください。
/**
* This coefficient determines what is the safe scale down factor.
*
* <p>If the Flink application previously failed before first checkpoint completed or we are starting new batch
* of {@link FlinkKafkaProducer011} from scratch without clean shutdown of the previous one,
* {@link FlinkKafkaProducer011} doesn't know what was the set of previously used Kafka's transactionalId's. In
* that case, it will try to play safe and abort all of the possible transactionalIds from the range of:
* {@code [0, getNumberOfParallelSubtasks() * kafkaProducersPoolSize * SAFE_SCALE_DOWN_FACTOR) }
*
* <p>The range of available to use transactional ids is:
* {@code [0, getNumberOfParallelSubtasks() * kafkaProducersPoolSize) }
*
* <p>This means that if we decrease {@code getNumberOfParallelSubtasks()} by a factor larger than
* {@code SAFE_SCALE_DOWN_FACTOR} we can have a left some lingering transaction.
*/
public static final int SAFE_SCALE_DOWN_FACTOR = 5;
kafkaタイムスタンプとflinkイベント時間を使用する
Kafka 0.10以降のバージョンでは、Kafkaメッセージはタイムスタンプをサポートします。このタイムスタンプは、イベント時間またはメッセージがKafkaに到着した時間です。イベント時間の概要については、https://blog.csdn.net/x950913/article/details/106246807を参照してください。
時間セマンティクスがflinkのイベント時間TimeCharacteristic.EventTimeに設定されている場合 FlinkKafkaConsumer010
、イベントタイムスタンプ付きのデータが送信されます。時間を設定するためのセマンティックコードは次のとおりです。
StreamExecutionEnvironment.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
KafkaデータにKafkaへの到着のタイムスタンプを含める場合は、本番環境でデータを定義する必要はありません。デフォルトはKafkaへの到着時刻です。
どのタイムスタンプを使用する場合でも、flink-kafkaリンカーの構成オブジェクトを使用して、setWriteTimestampToKafkaをtrueに呼び出す必要があります。
FlinkKafkaProducer010.FlinkKafkaProducer010Configuration config = FlinkKafkaProducer010.writeToKafkaWithTimestamps(streamWithTimestamps, topic, new SimpleStringSchema(), standardProps);
config.setWriteTimestampToKafka(true);
失われたデータ
設定は正確で1回限りの生産ですが、次の構成のデフォルト設定でもデータが失われる可能性があります。
acks
log.flush.interval.messages
log.flush.interval.ms
log.flush.*