Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
2016年12月15日
1
ヤフー株式会社 データ&サイエンスソリューション統括本部
データプラットフォーム本部 開発1部 パイプライン
森谷 大輔
噛み砕いてKafka Streams
自己紹介
• 氏名
• 森谷 大輔 @kokumutyoukan
• 業務
• 次世代データパイプラインの開発
• Kafka, Storm,
Cassandra, Elasticsearch
• 好き
• 横浜ベイスターズ
• ハングリータイガー(の会会長)
2
今日のゴール
• おっ、調べてみるかなという気になってもらう
• Kafka Streamsを触った内容を噛み砕いて紹介
• 布教というわけではない
• 気になるところあれば遠慮なくツッコんでください
3
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
4
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
5
Kafka Streams is 何
• ストリーム処理のアプリケーションを書くためのライブラリ
• Apache Kafka に同梱されている
• 0.10.0 からアップデートの目玉として追加 (2016年5月)
6
群雄割拠勢
Confluentが開発・導入促進を
頑張っている
ストリーム処理アプリケーションをつくるには
• よく必要になる「難しい機能」
• パーティショニング・拡張性
• 故障してもうまいこと復旧する(ステート管理)
• 遅れてやってきたデータもうまいこと処理する(時間の扱い)
• 再処理
• ウィンドウ集計
• 方法①:素のKafka Java APIを使う
• 方法②:ストリーム処理フレームワークを使う
• 方法③:Kafka Streamsを使う
7
①:素の Kafka Java API を使う
• お手軽
• Java ライブラリなのでアプリケーションを書いて jar にかためて java コ
マンドで起動さえすれば良い
• デプロイがシンプル
• 覚えることはAPIの使い方だけ
• ただし「難しい機能」を自分で考えて実装しなければならない
8
Consumer<byte[], byte[]> consumer = new KafkaConsumer<>(props);
consumer.subscribe(topics);
②:ストリーム処理フレームワークを使う
• Stormなど群雄割拠勢
• 「難しい機能」を含めリッチな機能が使える
• ただしフレームワークの専用クラスタが必要
• フレームワークならではの構成、設定、書き方
• デプロイ複雑
• 覚えることが多い
9
③:Kafka Streamsを使う
• 「Kafka Streamsはフレームワークではなく、ライブラリ」
• 「難しい機能」も抽象化されている
• 大体のパターンのストリーム処理アプリケーションを書くには充分
• リアルタイム性
• Spark Streamingのようなマイクロバッチではなく、Stormのような逐次処理(at least once)
• レイテンシ要求が厳しい案件でもOK
10
・サーバを分散処理モードで動かすためにセッティングし、
・フレームワークのとりきめに従ったアプリケーションの実装をし、
・専用のデプロイツールでデプロイしてはじめて分散処理
・ライブラリをクラスパスに含めてjarにかためてjavaコマンドうてば動く
比較
11
方法
(難しい機能)
実装の簡単さ
学習
コスト
運用(デプロイ)
コスト
① 素のKafka
Java APIを使う ✕ ◯ ◯
② ストリーム処理
フレームワークを使う ◯ ✕ ✕
③ Kafka Streams
◯ △ ◯
※独断と偏見
※ストリーム処理フレームワークにしかない機能もある
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
12
ことはじめ
13
• ビルド設定(maven)
• APIを選ぶ
• high-level DSL ←今回はこれ
• low-level API
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<version>0.10.0.1</version>
</dependency>
プログラム
14
@Test
public final void wordCount() {
KStreamBuilder builder = new KStreamBuilder();
KStream<String, String> queryStream
= builder.stream(stringSerde, stringSerde, “search-query-topic”); // 入力トピック名は複数指定可能
KStream<String, Long> wordCounts = queryStream
.flatMapValues(value -> Arrays.asList(value.split(“¥¥s+”))) // 空白区切り分割
.map((key, value) -> new KeyValue<>(value, value)) // key 毎カウント下準備
.countByKey(stringSerde, “Counts”) // KStream -> KTable
.toStream(); // KTable -> KStream
wordCounts.to(stringSerde, longSerde, “wordcount-output”); // sink トピックに結果を書く
KafkaStreams streams = new KafkaStreams(builder, props); // props は Kafka Streams の設定
streams.start(); // アプリケーション実行
}
入力・結果例
15
// 入力
producer.send(new ProducerRecord<>(“search-query-topic”, “ぬこ 飼い方”));
producer.send(new ProducerRecord<>(“search-query-topic”, “犬 飼い方”));
producer.send(new ProducerRecord<>(“search-query-topic”, “本当すこ ぬこ"));
consumer.subscribe(Arrays.asList("wordcount-output"));
while (true) {
ConsumerRecords<String, Long> records = consumer.poll(100);
for (ConsumerRecord<String, Long> record : records) {
System.out.println("record = " + record.key() + ", " + record.value());
}
}
// 出力
record = ぬこ, 1
record = 飼い方, 1
record = 犬, 1
record = 飼い方, 2
record = 本当すこ, 1
record = ぬこ, 2
アプリケーションの動作確認は
Kafka Unit Testを使うと便利
※Kafka 公式 FAQ 参照
KStream? KTable?
• KStream
• record streamを扱う場合はKStreamクラスを使う
• 自己完結のデータストリーム
• 例えばPVログ、サーバログ、ツイート
• KTable
• changelog streamを扱う場合はKTableクラスを使う
• 状態を持つ、keyで値が更新されるデータのストリーム
• 例えばこの単語が今までに何件出現したか、のようなデータ
• Stateとしてローカルに保持される
16
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
17
Time
• ストリームであるイベントが流れてきた時、そのイベントのタイムスタンプとしてどんな情
報を使うべきか
• 例えばイベントがツイートだとして、一時間毎のツイート数を計算したいといった場合、な
んのタイムスタンプ毎に計算する?
1. ユーザがツイートした瞬間
2. ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間
3. Kafkaに入った瞬間
4. Kafka Streamsがそのイベントを処理した瞬間
18
Tweet!
Twitter
API
my BE
server
Kafka Streams
① ② ③ ④
Time
• ストリームであるイベントが流れてきた時、そのイベントのタイムスタンプとしてどんな情
報を使うべきか
• 例えばイベントがツイートだとして、一時間毎のツイート数を計算したいといった場合、な
んのタイムスタンプ毎に計算する?
1. ユーザがツイートした瞬間
2. ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間
3. Kafkaに入った瞬間
4. Kafka Streamsがそのイベントを処理した瞬間
• 多くは1だと思うが、アプリケーションの仕様によって異なる
• Kafka Streamsでは設定項目 timestamp.extractor でどれを選択するか簡単に決められ
る
19
Kafka Streams的分類
• event-time
• ログ内の独自タイムスタンプの場合
• 「ユーザがツイートした瞬間」
• Kafka messageに付与されているタイムスタンプを使う場合
• 「ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間」
• broker設定 log.message.timestamp.type=CreateTime (デフォルト)
• このタイムスタンプはKafka0.10からMessageに付与される
• 0.9以前のproducerから投げると -1
• ingestion-time
• 「Kafkaに入った瞬間」
• log.message.timestamp.type=LogAppendTime だった場合
• そのイベントがKafka Brokerに入ったときの時刻がmessageタイムスタンプに付与
• processing-time
• 「Kafka Streamsがそのイベントを処理した瞬間」
20
timestamp.extractor
21
Time分類 timestamp.extractor
event-time(独自) 自分で実装する
event-time(message) ConsumerRecordTimestampExtractor
ingestion-time ConsumerRecordTimestampExtractor
processing-time WallclockTimestampExtractor
import java.util.Properties;
import org.apache.kafka.streams.StreamsConfig;
Properties props = new Properties();
props.put(StreamsConfig.TIMESTAMP_EXTRACTOR_CLASS_CONFIG,
WallclockTimestampExtractor.class.getName());
設定例
独自クラス実装例
22
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.streams.processor.TimestampExtractor;
// TimestampExtractorインタフェースを実装する
public class MyEventTimeExtractor implements TimestampExtractor {
@Override public long extract(ConsumerRecord<Object, Object> record) {
// ログをパースしてtimestampを取り出す
Foo myPojo = (Foo) record.value();
if (myPojo != null) {
return myPojo.getTimestampInMillis();
} else {
// valueがnullだったらとりあえず現在時刻をいれておく
return System.currentTimeMillis();
}
}
}
http://docs.confluent.io/3.0.0/streams/developer-guide.html#timestamp-extractor (コメント以外引用)
Window
23
• Tumbling time window
• 5分毎のユーザ毎のPV数とか
• Hopping time window
• 1つのイベントが複数のウィンドウにまたがる
KStream<String, String> viewsByUser = ユーザIDがkeyのPVログStreamなど;
KTable<Windowed<String>, Long> userCounts =
viewsByUser.countByKey(TimeWindows.of(”WindowName", 5 * 60 * 1000L));
TimeWindows.of(”WindowName", 5 * 60 * 1000L).advanceBy(60 * 1000L);
Join
24
• ストリーム処理でよくやるストリームとテーブルのJoinができる
• KTableはローカルにあり、常に最新である
• メッセージ処理毎にネットワークを超えてKVSを叩く必要も、鮮度を諦めて定期的にRDBを
メモリにロードする必要もない
KStream<String, String> voteRegionStream = ...(“vote-topic”)
KTable<String, String> partyTable = ...("party-topic");
KStream<String, String> voteParty
= voteRegionStream.leftJoin(
partyTable, (region, party) -> region + ”," + party);
k: Hillary v: California k: candidate v: party
Hillary Democratic
Trump Republican
k: Hillary v: California, Democratic
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
25
Kafka Streamsで開発してみた
• Kafkaクラスタから引いた全メッセージをグルーピング、ウィンドウ集計して指標をsinkに
書くシンプルなアプリ
• ローカルでテストは通った、本番デプロイいこう
• バグを踏む:Kafka-4160 (´・ω・`)
• Kafka Streamsのフォアグラウンドスレッドとバッググラウンド
ハートビートスレッドの間に単一のロックがある
• タスク生成中にハートビートをブロックするのでタスク生成が長い
とセッションタイムアウトを超える
• consumerがグループから追い出されて再度タスク生成を始める
• 永遠に繰り返してデッドロックみたくなる
• 入力パーティション数が少ないと問題にならないのだが
本番では1topicあたり最大60あったため本番で初めて発覚した
26
続き
• バグは Kafka 0.10.1.0 で解消されたよ!(今client, server共に 0.10.0.1)
• Kafka Streams をバージョンアップすれば解決しそう
• > Apps built with Kafka Streams 0.10.1 only work against Kafka clusters running
0.10.1+.
• 古いサーバに対しても互換性なんとかしたいとは書いてあった
• 0.10.0.1のKafkaにバグフィックスだけパッチ当ててアプリに入れるか・・・
• 対象コードが 0.10.1 で大きく変わってて厳しい
• やっぱりサーバあげよう ← イマココ
27
___________
/|:: ┌──────┐ ::|
/. |:: | Exception | ::| / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
|.... |:: | Use 0.10.1 !| ::| | マイナーバージョンアップなら…アレ?
|.... |:: | .| ::| \_ ______
|.... |:: └──────┘ ::| ∨
\_| ┌────┐ .| ∧∧
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ( _)
/ ̄ ̄ ̄ ̄ ̄旦 ̄(_, )
/ \
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|、_)
 ̄| ̄| ̄ ̄ ̄ ̄ ̄ ̄|
思ったこと
• 向くユースケースなら向く
• Kafka Streamsのコンセプトがわかってきてからシステム設計した方がいいかも
• 既にカッチリ決まった要件にKafka Streamsを合わせようとするとハックするはめになるかも
• ライブラリならアプリケーション開発を楽にしてくれなくちゃいけない
• Kafkaの素のハイレベルAPIがそもそもかなりちょうどいい抽象化
• Kafka Streams APIの利点が活かせるかどうか
• インターナルトピックをかなり大量に作ることを想定している(アプリのバージョンアップご
とにトピックは増える)
• 小さめのサービス専用クラスタとかならいいが、マルチテナント向けのクラスタだとちょっと気持ち悪
いかも
• 現在は1 Kafkaクラスタしか指定できないが将来的には複数可能になるかも
28
アジェンダ
• 概要
• Word Count
• Time, Window, Join
• つかってみた
• まとめ
29
まとめ
• Kafka Streamsはストリーム処理のアプリケーションを実装するためのライブ
ラリ
• シンプルながらストリーム処理でよく必要になる、自分で実装するには難し
い機能を実現する
• 時間軸に何を使うか開発者が選択できる
• Kafka Streamsが便利に使えるようにシステム設計をすると吉
30
Appendix
31
Kafka Streamsはどこで動くの?
32
• consumerアプリケーション
• 普通はKafkaクラスタの(物理的に)近くのアプリケーション専用サーバ上でJavaプロセスとして動
かすと思う
• ライブラリなので何でもできるが、Kafkaとしか接続しないように全体設計すると楽そう
Kafkaクラスタ
source topic
internal topic
sink topic
Kafka
Streams
Kafka
Connect 等
Kafka
Connect 等
Configuration
33
import java.util.Properties;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ConsumerConfig;
Properties settings = new Properties();
settings.put(StreamsConfig.APPLICATION_ID_CONFIG, “my-app”); // StreamConfigのこの3つは必須
settings.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, ”localhost:9092");
settings.put(StreamsConfig.ZOOKEEPER_CONNECT_CONFIG, ”localhost:2181");
settings.put(ProducerConfig...., “”); // 必須でない
settings.put(ConsumerConfig...., “”); // 必須でない
application.id アプリケーション認識名. consumer group名やinternal topic名等に利用される.
bootstrap.servers 接続するKafkaクラスタのhost/portペアのリスト.
zookeeper.connect 接続するZooKeeperのコネクション文字列(host:port/chroot).
num.stream.threads ストリーム処理のために使うスレッド数.
replication.factor internal topicを作るときのレプリケーションファクタ
state.dir State Storeのディレクトリパス
timestamp.extractor 後述
フォールトトレラント
• 故障時にStateを復旧させるため、Kafkaクラスタにchangelog topicという内部topicが作ら
れる
34
Node
Task
source part-1
changelog part-1
Node
Task
source part-0
changelog part-0
フォールトトレラント
• 故障時にStateを復旧させるため、Kafkaクラスタにchangelog topicという内部topicが作ら
れる
35
Node
Task
source part-1
changelog part-1
Node
Tasksource part-0
changelog part-0
Task
changelog topic(おまけ)
• topicはKafka Streamsアプリケーションの実行時に自動で作成される
• 手動でtopicを作るときと同じような感じで、Kafka設定
auto.create.topics.enable=falseでも作成される
• topic設定はcompact
• 同じkeyで頻繁にvalueが変わるはずだから
• タスク数分パーティションが作られる
36
プログラム(full)
37
@Test
public final void wordCount() {
final Serde<String> stringSerde = Serdes.String(); // Serde is Serializer/Deserializerの略、Kafka共通のクラス
final Serde<Long> longSerde = Serdes.Long(); // 基本的なビルトインをSerdesから呼べる、もちろん自作可能
KStreamBuilder builder = new KStreamBuilder();
// 入力名からKStreamを作る. 1: key Serde, 2: value Serde, 3: 入力トピック名(複数指定可能)
KStream<String, String> queryStream = builder.stream(stringSerde, stringSerde, “search-query-topic”);
KStream<String, Long> wordCounts = queryStream
// valueに対して空白区切りで文字列を分割して次に送る処理
.flatMapValues(value -> Arrays.asList(value.split(“¥¥s+”)))
// key毎カウントしたいからkeyにvalueを入れる
.map((key, value) -> new KeyValue<>(value, value))
.countByKey(stringSerde, “Counts”) // KStream -> KTable、第二引数はKTable名
.toStream(); // KTable -> KStream
wordCounts.to(stringSerde, longSerde, “wordcount-output”); // sinkトピックに結果を書く
KafkaStreams streams = new KafkaStreams(builder, props); // propsはKafka StreamsやClientの設定Properties
streams.start(); // アプリケーション実行
}
比較(full)
38
方法
(難しい機能)
実装の簡単さ
学習
コスト
運用(デプロイ)
コスト
実績
ドキュメント
充実度
① 素のKafka
Java APIを使う ✕ ◯ ◯ ◯ ◯
② ストリーム処理
フレームワークを使う ◯ ✕ ✕ △
(差異が大きい)
△?
③ Kafka Streams
◯ △ ◯ ✕ △
※独断と偏見

More Related Content

噛み砕いてKafka Streams #kafkajp

  • 2. 自己紹介 • 氏名 • 森谷 大輔 @kokumutyoukan • 業務 • 次世代データパイプラインの開発 • Kafka, Storm, Cassandra, Elasticsearch • 好き • 横浜ベイスターズ • ハングリータイガー(の会会長) 2
  • 3. 今日のゴール • おっ、調べてみるかなという気になってもらう • Kafka Streamsを触った内容を噛み砕いて紹介 • 布教というわけではない • 気になるところあれば遠慮なくツッコんでください 3
  • 4. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 4
  • 5. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 5
  • 6. Kafka Streams is 何 • ストリーム処理のアプリケーションを書くためのライブラリ • Apache Kafka に同梱されている • 0.10.0 からアップデートの目玉として追加 (2016年5月) 6 群雄割拠勢 Confluentが開発・導入促進を 頑張っている
  • 7. ストリーム処理アプリケーションをつくるには • よく必要になる「難しい機能」 • パーティショニング・拡張性 • 故障してもうまいこと復旧する(ステート管理) • 遅れてやってきたデータもうまいこと処理する(時間の扱い) • 再処理 • ウィンドウ集計 • 方法①:素のKafka Java APIを使う • 方法②:ストリーム処理フレームワークを使う • 方法③:Kafka Streamsを使う 7
  • 8. ①:素の Kafka Java API を使う • お手軽 • Java ライブラリなのでアプリケーションを書いて jar にかためて java コ マンドで起動さえすれば良い • デプロイがシンプル • 覚えることはAPIの使い方だけ • ただし「難しい機能」を自分で考えて実装しなければならない 8 Consumer<byte[], byte[]> consumer = new KafkaConsumer<>(props); consumer.subscribe(topics);
  • 9. ②:ストリーム処理フレームワークを使う • Stormなど群雄割拠勢 • 「難しい機能」を含めリッチな機能が使える • ただしフレームワークの専用クラスタが必要 • フレームワークならではの構成、設定、書き方 • デプロイ複雑 • 覚えることが多い 9
  • 10. ③:Kafka Streamsを使う • 「Kafka Streamsはフレームワークではなく、ライブラリ」 • 「難しい機能」も抽象化されている • 大体のパターンのストリーム処理アプリケーションを書くには充分 • リアルタイム性 • Spark Streamingのようなマイクロバッチではなく、Stormのような逐次処理(at least once) • レイテンシ要求が厳しい案件でもOK 10 ・サーバを分散処理モードで動かすためにセッティングし、 ・フレームワークのとりきめに従ったアプリケーションの実装をし、 ・専用のデプロイツールでデプロイしてはじめて分散処理 ・ライブラリをクラスパスに含めてjarにかためてjavaコマンドうてば動く
  • 11. 比較 11 方法 (難しい機能) 実装の簡単さ 学習 コスト 運用(デプロイ) コスト ① 素のKafka Java APIを使う ✕ ◯ ◯ ② ストリーム処理 フレームワークを使う ◯ ✕ ✕ ③ Kafka Streams ◯ △ ◯ ※独断と偏見 ※ストリーム処理フレームワークにしかない機能もある
  • 12. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 12
  • 13. ことはじめ 13 • ビルド設定(maven) • APIを選ぶ • high-level DSL ←今回はこれ • low-level API <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-streams</artifactId> <version>0.10.0.1</version> </dependency>
  • 14. プログラム 14 @Test public final void wordCount() { KStreamBuilder builder = new KStreamBuilder(); KStream<String, String> queryStream = builder.stream(stringSerde, stringSerde, “search-query-topic”); // 入力トピック名は複数指定可能 KStream<String, Long> wordCounts = queryStream .flatMapValues(value -> Arrays.asList(value.split(“¥¥s+”))) // 空白区切り分割 .map((key, value) -> new KeyValue<>(value, value)) // key 毎カウント下準備 .countByKey(stringSerde, “Counts”) // KStream -> KTable .toStream(); // KTable -> KStream wordCounts.to(stringSerde, longSerde, “wordcount-output”); // sink トピックに結果を書く KafkaStreams streams = new KafkaStreams(builder, props); // props は Kafka Streams の設定 streams.start(); // アプリケーション実行 }
  • 15. 入力・結果例 15 // 入力 producer.send(new ProducerRecord<>(“search-query-topic”, “ぬこ 飼い方”)); producer.send(new ProducerRecord<>(“search-query-topic”, “犬 飼い方”)); producer.send(new ProducerRecord<>(“search-query-topic”, “本当すこ ぬこ")); consumer.subscribe(Arrays.asList("wordcount-output")); while (true) { ConsumerRecords<String, Long> records = consumer.poll(100); for (ConsumerRecord<String, Long> record : records) { System.out.println("record = " + record.key() + ", " + record.value()); } } // 出力 record = ぬこ, 1 record = 飼い方, 1 record = 犬, 1 record = 飼い方, 2 record = 本当すこ, 1 record = ぬこ, 2 アプリケーションの動作確認は Kafka Unit Testを使うと便利 ※Kafka 公式 FAQ 参照
  • 16. KStream? KTable? • KStream • record streamを扱う場合はKStreamクラスを使う • 自己完結のデータストリーム • 例えばPVログ、サーバログ、ツイート • KTable • changelog streamを扱う場合はKTableクラスを使う • 状態を持つ、keyで値が更新されるデータのストリーム • 例えばこの単語が今までに何件出現したか、のようなデータ • Stateとしてローカルに保持される 16
  • 17. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 17
  • 18. Time • ストリームであるイベントが流れてきた時、そのイベントのタイムスタンプとしてどんな情 報を使うべきか • 例えばイベントがツイートだとして、一時間毎のツイート数を計算したいといった場合、な んのタイムスタンプ毎に計算する? 1. ユーザがツイートした瞬間 2. ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間 3. Kafkaに入った瞬間 4. Kafka Streamsがそのイベントを処理した瞬間 18 Tweet! Twitter API my BE server Kafka Streams ① ② ③ ④
  • 19. Time • ストリームであるイベントが流れてきた時、そのイベントのタイムスタンプとしてどんな情 報を使うべきか • 例えばイベントがツイートだとして、一時間毎のツイート数を計算したいといった場合、な んのタイムスタンプ毎に計算する? 1. ユーザがツイートした瞬間 2. ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間 3. Kafkaに入った瞬間 4. Kafka Streamsがそのイベントを処理した瞬間 • 多くは1だと思うが、アプリケーションの仕様によって異なる • Kafka Streamsでは設定項目 timestamp.extractor でどれを選択するか簡単に決められ る 19
  • 20. Kafka Streams的分類 • event-time • ログ内の独自タイムスタンプの場合 • 「ユーザがツイートした瞬間」 • Kafka messageに付与されているタイムスタンプを使う場合 • 「ツイートをAPIからバックエンドサーバが受け取ってKafkaに投げた瞬間」 • broker設定 log.message.timestamp.type=CreateTime (デフォルト) • このタイムスタンプはKafka0.10からMessageに付与される • 0.9以前のproducerから投げると -1 • ingestion-time • 「Kafkaに入った瞬間」 • log.message.timestamp.type=LogAppendTime だった場合 • そのイベントがKafka Brokerに入ったときの時刻がmessageタイムスタンプに付与 • processing-time • 「Kafka Streamsがそのイベントを処理した瞬間」 20
  • 21. timestamp.extractor 21 Time分類 timestamp.extractor event-time(独自) 自分で実装する event-time(message) ConsumerRecordTimestampExtractor ingestion-time ConsumerRecordTimestampExtractor processing-time WallclockTimestampExtractor import java.util.Properties; import org.apache.kafka.streams.StreamsConfig; Properties props = new Properties(); props.put(StreamsConfig.TIMESTAMP_EXTRACTOR_CLASS_CONFIG, WallclockTimestampExtractor.class.getName()); 設定例
  • 22. 独自クラス実装例 22 import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.streams.processor.TimestampExtractor; // TimestampExtractorインタフェースを実装する public class MyEventTimeExtractor implements TimestampExtractor { @Override public long extract(ConsumerRecord<Object, Object> record) { // ログをパースしてtimestampを取り出す Foo myPojo = (Foo) record.value(); if (myPojo != null) { return myPojo.getTimestampInMillis(); } else { // valueがnullだったらとりあえず現在時刻をいれておく return System.currentTimeMillis(); } } } http://docs.confluent.io/3.0.0/streams/developer-guide.html#timestamp-extractor (コメント以外引用)
  • 23. Window 23 • Tumbling time window • 5分毎のユーザ毎のPV数とか • Hopping time window • 1つのイベントが複数のウィンドウにまたがる KStream<String, String> viewsByUser = ユーザIDがkeyのPVログStreamなど; KTable<Windowed<String>, Long> userCounts = viewsByUser.countByKey(TimeWindows.of(”WindowName", 5 * 60 * 1000L)); TimeWindows.of(”WindowName", 5 * 60 * 1000L).advanceBy(60 * 1000L);
  • 24. Join 24 • ストリーム処理でよくやるストリームとテーブルのJoinができる • KTableはローカルにあり、常に最新である • メッセージ処理毎にネットワークを超えてKVSを叩く必要も、鮮度を諦めて定期的にRDBを メモリにロードする必要もない KStream<String, String> voteRegionStream = ...(“vote-topic”) KTable<String, String> partyTable = ...("party-topic"); KStream<String, String> voteParty = voteRegionStream.leftJoin( partyTable, (region, party) -> region + ”," + party); k: Hillary v: California k: candidate v: party Hillary Democratic Trump Republican k: Hillary v: California, Democratic
  • 25. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 25
  • 26. Kafka Streamsで開発してみた • Kafkaクラスタから引いた全メッセージをグルーピング、ウィンドウ集計して指標をsinkに 書くシンプルなアプリ • ローカルでテストは通った、本番デプロイいこう • バグを踏む:Kafka-4160 (´・ω・`) • Kafka Streamsのフォアグラウンドスレッドとバッググラウンド ハートビートスレッドの間に単一のロックがある • タスク生成中にハートビートをブロックするのでタスク生成が長い とセッションタイムアウトを超える • consumerがグループから追い出されて再度タスク生成を始める • 永遠に繰り返してデッドロックみたくなる • 入力パーティション数が少ないと問題にならないのだが 本番では1topicあたり最大60あったため本番で初めて発覚した 26
  • 27. 続き • バグは Kafka 0.10.1.0 で解消されたよ!(今client, server共に 0.10.0.1) • Kafka Streams をバージョンアップすれば解決しそう • > Apps built with Kafka Streams 0.10.1 only work against Kafka clusters running 0.10.1+. • 古いサーバに対しても互換性なんとかしたいとは書いてあった • 0.10.0.1のKafkaにバグフィックスだけパッチ当ててアプリに入れるか・・・ • 対象コードが 0.10.1 で大きく変わってて厳しい • やっぱりサーバあげよう ← イマココ 27 ___________ /|:: ┌──────┐ ::| /. |:: | Exception | ::| / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ |.... |:: | Use 0.10.1 !| ::| | マイナーバージョンアップなら…アレ? |.... |:: | .| ::| \_ ______ |.... |:: └──────┘ ::| ∨ \_| ┌────┐ .| ∧∧  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ( _) / ̄ ̄ ̄ ̄ ̄旦 ̄(_, ) / \ | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|、_)  ̄| ̄| ̄ ̄ ̄ ̄ ̄ ̄|
  • 28. 思ったこと • 向くユースケースなら向く • Kafka Streamsのコンセプトがわかってきてからシステム設計した方がいいかも • 既にカッチリ決まった要件にKafka Streamsを合わせようとするとハックするはめになるかも • ライブラリならアプリケーション開発を楽にしてくれなくちゃいけない • Kafkaの素のハイレベルAPIがそもそもかなりちょうどいい抽象化 • Kafka Streams APIの利点が活かせるかどうか • インターナルトピックをかなり大量に作ることを想定している(アプリのバージョンアップご とにトピックは増える) • 小さめのサービス専用クラスタとかならいいが、マルチテナント向けのクラスタだとちょっと気持ち悪 いかも • 現在は1 Kafkaクラスタしか指定できないが将来的には複数可能になるかも 28
  • 29. アジェンダ • 概要 • Word Count • Time, Window, Join • つかってみた • まとめ 29
  • 30. まとめ • Kafka Streamsはストリーム処理のアプリケーションを実装するためのライブ ラリ • シンプルながらストリーム処理でよく必要になる、自分で実装するには難し い機能を実現する • 時間軸に何を使うか開発者が選択できる • Kafka Streamsが便利に使えるようにシステム設計をすると吉 30
  • 32. Kafka Streamsはどこで動くの? 32 • consumerアプリケーション • 普通はKafkaクラスタの(物理的に)近くのアプリケーション専用サーバ上でJavaプロセスとして動 かすと思う • ライブラリなので何でもできるが、Kafkaとしか接続しないように全体設計すると楽そう Kafkaクラスタ source topic internal topic sink topic Kafka Streams Kafka Connect 等 Kafka Connect 等
  • 33. Configuration 33 import java.util.Properties; import org.apache.kafka.streams.StreamsConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ConsumerConfig; Properties settings = new Properties(); settings.put(StreamsConfig.APPLICATION_ID_CONFIG, “my-app”); // StreamConfigのこの3つは必須 settings.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, ”localhost:9092"); settings.put(StreamsConfig.ZOOKEEPER_CONNECT_CONFIG, ”localhost:2181"); settings.put(ProducerConfig...., “”); // 必須でない settings.put(ConsumerConfig...., “”); // 必須でない application.id アプリケーション認識名. consumer group名やinternal topic名等に利用される. bootstrap.servers 接続するKafkaクラスタのhost/portペアのリスト. zookeeper.connect 接続するZooKeeperのコネクション文字列(host:port/chroot). num.stream.threads ストリーム処理のために使うスレッド数. replication.factor internal topicを作るときのレプリケーションファクタ state.dir State Storeのディレクトリパス timestamp.extractor 後述
  • 36. changelog topic(おまけ) • topicはKafka Streamsアプリケーションの実行時に自動で作成される • 手動でtopicを作るときと同じような感じで、Kafka設定 auto.create.topics.enable=falseでも作成される • topic設定はcompact • 同じkeyで頻繁にvalueが変わるはずだから • タスク数分パーティションが作られる 36
  • 37. プログラム(full) 37 @Test public final void wordCount() { final Serde<String> stringSerde = Serdes.String(); // Serde is Serializer/Deserializerの略、Kafka共通のクラス final Serde<Long> longSerde = Serdes.Long(); // 基本的なビルトインをSerdesから呼べる、もちろん自作可能 KStreamBuilder builder = new KStreamBuilder(); // 入力名からKStreamを作る. 1: key Serde, 2: value Serde, 3: 入力トピック名(複数指定可能) KStream<String, String> queryStream = builder.stream(stringSerde, stringSerde, “search-query-topic”); KStream<String, Long> wordCounts = queryStream // valueに対して空白区切りで文字列を分割して次に送る処理 .flatMapValues(value -> Arrays.asList(value.split(“¥¥s+”))) // key毎カウントしたいからkeyにvalueを入れる .map((key, value) -> new KeyValue<>(value, value)) .countByKey(stringSerde, “Counts”) // KStream -> KTable、第二引数はKTable名 .toStream(); // KTable -> KStream wordCounts.to(stringSerde, longSerde, “wordcount-output”); // sinkトピックに結果を書く KafkaStreams streams = new KafkaStreams(builder, props); // propsはKafka StreamsやClientの設定Properties streams.start(); // アプリケーション実行 }
  • 38. 比較(full) 38 方法 (難しい機能) 実装の簡単さ 学習 コスト 運用(デプロイ) コスト 実績 ドキュメント 充実度 ① 素のKafka Java APIを使う ✕ ◯ ◯ ◯ ◯ ② ストリーム処理 フレームワークを使う ◯ ✕ ✕ △ (差異が大きい) △? ③ Kafka Streams ◯ △ ◯ ✕ △ ※独断と偏見