私はカフカストリームをしようとしています。私は重複したメッセージをカウントしていますシンプルなアプリケーションを作成します。
メッセージ:
2019-02-27-11:16:56 :: session:prod-111656 :: Msg => Hello World: 2491
2019-02-27-11:16:56 :: session:prod-111656 :: Msg => Hello World: 2492
等
私はでこのようなメッセージを分割しようとしていますsession:prod-xxxx
。キーとして使用してください。そしてsession:prod-xxxx+Hello World: xxxx
値として使用します。そして、キーによるグループ、および各セッションで重複してしまったメッセージを参照してください。
ここで、コードは次のとおりです。
KStream<String, String> textLines = builder.stream("RegularProducer");
KTable<String, Long> ktable = textLines.map(
(String key, String value) -> {
try {
String[] parts = value.split("::");
String sessionId = parts[1];
String message = ((parts[2]).split("=>"))[1];
message = sessionId+":"+message;
return new KeyValue<String,String>(sessionId.trim().toLowerCase(), message.trim().toLowerCase());
} catch (Exception e) {
return new KeyValue<String,String>("Invalid-Message".trim().toLowerCase(), "Invalid Message".trim().toLowerCase());
}
})
.groupBy((key,value) -> value)
.count().filter(
(String key, Long value) -> {
return value > 1;
}
);
ktable.toStream().to("RegularProducerDuplicates",
Produced.with(Serdes.String(), Serdes.Long()));
Topology topology = builder.build();
topology.describe();
KafkaStreams streams = new KafkaStreams(topology, props);
streams.start();
KTableトピックRegularProducerDuplicatesが生成されます。私はそれを見るために、コンソール・コンシューマを使用する場合でも、それはエラーでクラッシュします。それから私は、コンソール消費者に--skip-のメッセージオンエラーフラグを使用します。今、私はこれらのような行の数千人を見ます
session:prod-111656 : hello world: 994 [2019-02-28 16:25:18,081] ERROR Error processing message, skipping this message: (kafka.tools.ConsoleConsumer$)
org.apache.kafka.common.errors.SerializationException: Size of data received by LongDeserializer is not 8
誰の助けは私が間違ってここに何が起こっていることはできますか?
あなたのカフカストリームアプリケーションはokですし、正常に動作します。
バグはであるkafka-console-consumer
(kafka.tools.ConsoleConsumer
スクリプトの実装ロジックというクラスです)。
これは、適切に処理しないnull
直列化復元時に。それを取得する際null
のメッセージの値またはキーとしてデフォルト値を設定し(バイト配列ことを示すnull
文字列)。あなたがソースコードをチェックする場合は、次の関数を見つけることができます
def write(deserializer: Option[Deserializer[_]], sourceBytes: Array[Byte]) {
val nonNullBytes = Option(sourceBytes).getOrElse("null".getBytes(StandardCharsets.UTF_8))
val convertedBytes = deserializer.map(_.deserialize(null, nonNullBytes).toString.
getBytes(StandardCharsets.UTF_8)).getOrElse(nonNullBytes)
output.write(convertedBytes)
}
それはヌル(あるsourceBytesなったとき、あなたは見ることができますどのようにsourceBytes==null
それがそのためにデフォルト値を設定し、逆シリアル化のために):
val nonNullBytes = Option(sourceBytes).getOrElse("null".getBytes(StandardCharsets.UTF_8))
あなたのケースではそれがあります"null".getBytes(StandardCharsets.UTF_8)
。次に、との直列化復元の試みがあるorg.apache.kafka.common.serialization.LongDeserializer
(自分の価値デシリアライザは)。LongDeserializer
非常にバイトの配列のサイズを先頭にチェックします。今では4(バイトの表現であるnull
)と、例外がスローされます。
あなたの使用例StringDeserializerのためならば、それは適切にそれをデシリアライズしませんが、それはバイトの配列の長さをチェックしませんので、少なくともそれは、例外をスローしません。
長い話を短く:ConsoleConsumerのフォーマッタ、印刷するための責任がある、かなり印刷のためにいくつかのデシリアライザ(LongDeserializer、IntegerDeserializer)で扱うことができないいくつかのデフォルト値を設定しました
、なぜあなたのアプリケーションの農産物についてはnull
、いくつかのキーの値:
KTable:filter
異なるセマンティックを持っていますKStream::filter
。KTableのJavadocによると:
切断されますレコードごとに墓石レコードが転送される(すなわち、与えられた述語を満たしていません)。
あなたのためにfilter
、ときcount <= 1
、それは渡されnull
たキーの値を。