序文
Kafka のベスト プラクティス、関連するもの
- 一般的な使用シナリオ
- Kafka の使用に関するベスト プラクティス
Kafka の一般的な使用シナリオ
データストリーミング
Kafka は、Spark、Flink、Flume などの複数の主流ストリーミング データ処理テクノロジに接続できます。Kafkaの高いスループットを活かして、Kafkaを介して大量のデータをアプリケーション側からストリームデータ処理エンジンに送信する伝送路を確立し、データの処理・分析後のバックエンドのビッグデータ分析や、 AIモデルトレーニング等の事業。
ログプラットフォーム
Kafka で最もよく使用されるシナリオであり、私が最もよく知っているシナリオはログ分析システムです。典型的な実装は、ログ収集のためにクライアント側にログ コレクター (Fluentd、Filebeat、Logstash など) をデプロイし、データを Kafka に送信し、バックエンド ES などを通じてデータ計算を実行することです。 . を作成し、Kibana などの表示レイヤーを構築します。統計分析データのプレゼンテーション。
モノのインターネット
モノのインターネット (IoT) は、貴重なユースケースが出現するにつれて注目を集めています。ただし、重要な課題は、デバイスとマシンを統合して、リアルタイムかつ大規模にデータを処理することです。Apache Kafka® とその周囲のエコシステム (Kafka Connect、Kafka Streams など) は、このようなデータセットの統合と処理に最適なテクノロジとなっています。
Kafka は、消費者向け IoT や産業用モノのインターネット (IIoT) を含む多くの IoT 導入ですでに使用されています。ほとんどのシナリオでは、リアルタイムの双方向通信とデータ処理をサポートする、信頼性が高く、スケーラブルで安全なエンドツーエンドの統合が必要です。具体的な使用例は次のとおりです。
- コネクテッドビークルインフラストラクチャ
- スマートシティとスマートホーム
- スマート リテールと顧客 360
- スマート製造
具体的な実装アーキテクチャを次の図に示します。
使用するベストプラクティス
信頼性のベストプラクティス
プロデューサとコンシューマの構成に基づいて異なる信頼性を満たす
プロデューサー少なくとも一度は
プロデューサは を設定する必要がありrequest.required.acks = ALL
、サーバー側のマスター ノードは正常に書き込み、バックアップ ノードは正常に同期して応答を返します。
消費者が少なくとも一度は
メッセージを受信した後、コンシューマーは最初に対応するビジネス オペレーションを実行し、メッセージが処理されたことを示すためにコミットする必要があります。この処理方法により、ビジネス処理が失敗したときにメッセージを再度コンシュームできるようになります。コミットアクションが手動で制御されるようにするには、コンシューマenable.auto.commit
パラメータ。False
プロデューサーAt Most Once
メッセージを最大 1 回配信できるようにするには、メッセージをrequest.required.acks = 0
同時に設定する必要がありますretries = 0
。ここでの原則は、プロデューサーは例外が発生したときに再試行せず、ブローカーが書き込みの成功に応答するかどうかを考慮しないことです。
消費者は一度は
メッセージを最大 1 回だけ確実に消費できるようにするには、コンシューマは、メッセージの受信後にメッセージが処理されたことを示すためにコミットし、対応するビジネス オペレーションを実行する必要があります。ここでの原則は、コンシューマーは実際のビジネスの処理結果を気にする必要はなく、メッセージを受信したらすぐにコミットして、メッセージが正常に処理されたことをブローカーに伝えるということです。コミットアクションが手動で制御されるようにするには、コンシューマenable.auto.commit
パラメータ。False
プロデューサーは一度だけ
Kafka のバージョン 0.11 以降、冪等メッセージenable.idempotence=true
のセマンティクスが追加され、パラメータを設定することで単一パーティションのメッセージ冪等性を実現できます。
トピックに複数のパーティションが含まれる場合、または冪等性を確保するために複数のメッセージを 1 つのトランザクションにカプセル化する必要がある場合は、次のようにトランザクション制御を強化する必要があります。
// 开启幂等控制参数
producerProps.put("enbale.idempotence", "true");
// 初始化事务
producer.initTransactions();
// 设置事务 ID
producerProps.put("transactional.id", "id-001");
try{
// 开始事务,并在事务中发送 2 条消息
producer.beginTranscation();
producer.send(record0);
producer.send(record1);
// 提交事务
producer.commitTranscation();
} catch( Exception e ) {
producer.abortTransaction();
producer.close();
}
消費者は一度だけ
コンシューマがプロデューサがコミットしたメッセージのみを消費するように設定する必要があります。コンシューマ ビジネスは、データベースにメッセージを永続化してからコミットを送信するなど、メッセージの繰り返し処理を避けるためにトランザクション性を確保する必要がありisolation.level=read_committed
ます。enable.auto.commit = false
サーバーに。
ビジネス シナリオに応じて適切なセマンティクスを選択する
At Least Once セマンティクスを使用して、少数の繰り返しメッセージを受け入れることができるビジネスをサポートします
At Least Once は最も一般的に使用されるセマンティクスであり、パフォーマンスと信頼性のバランスが取れた状態でメッセージが可能な限り送信および消費されるようにすることができ、デフォルト モードとして使用できます。ビジネス側は、一意のビジネス主キーをメッセージ本文に追加することで冪等性を確保し、同じビジネス主キーを持つメッセージがコンシューマ側で 1 回だけ処理されるようにすることもできます。
Exactly Once セマンティック サポートを使用するには、強力な冪等のビジネスが必要です
Exactly Once セマンティクスは通常、繰り返しが絶対に許されない重要なビジネスに使用され、典型的なケースは注文と支払い関連のシナリオです。
重要ではないビジネスをサポートするために At Most Once セマンティクスを使用する
通常、セマンティクスが重要でないビジネスで使用されると、ビジネスはメッセージの損失に敏感ではなくなり、メッセージの生成と消費が正常に行われることを確認するだけで済みます。At Most Once セマンティクスが使用される典型的なシナリオはメッセージ通知であり、少数のメッセージが欠落しても影響はほとんどありませんが、通知を繰り返し送信するとユーザー エクスペリエンスが低下します。
パフォーマンスチューニングのベストプラクティス
トピックのパーティション数を適切に設定する
以下に、パーティションによるパフォーマンスのチューニングに推奨されるディメンションをまとめます。理論的な分析とストレス テストに基づいてシステム全体のパフォーマンスをチューニングすることをお勧めします。
寸法を考慮する | 説明する |
---|---|
スループット | パーティションの数を増やすと、メッセージ消費の同時実行性が向上します。システムのボトルネックが消費側にあり、消費側を水平に拡張できる場合、パーティションを増やすとシステムのスループットが向上します。Kafka の各トピックの各パーティションは、独立したメッセージ処理チャネルです。パーティション内のメッセージは、同時に 1 つのコンシューマ グループによってのみ使用できます。コンシューマ グループの数がパーティションの数を超えると、冗長コンシューマ グループ Idle が現れる。 |
メッセージシーケンス | Kafka はパーティション内のメッセージの順序を保証できますが、パーティション間のメッセージの順序は保証できないため、パーティションを追加するときは、メッセージの順序がビジネスに与える影響を考慮する必要があります。 |
インスタンスパーティションの上限 | パーティションを増やすと、メモリ、IO、ファイル ハンドルなどの基礎となるリソースがより多く消費されます。トピックのパーティション数を計画するときは、Kafka クラスターがサポートできるパーティションの上限を考慮する必要があります。 |
プロデューサ、コンシューマ、パーティション間の関係の説明。
バッチサイズを適切に設定する
トピックに複数のパーティションがある場合、プロデューサーは最初にどのパーティションにメッセージを送信するかを確認する必要があります。複数のメッセージを同じパーティションに送信する場合、プロデューサー クライアントは関連するメッセージをバッチにパッケージ化し、バッチでサーバーに送信します。一般に、バッチが小さいと、プロデューサー クライアントが大量のリクエストを生成し、クライアントとサーバーでリクエスト キューがキューに入れられるため、全体としてメッセージの送信と消費の遅延が押し上げられます。
適切なバッチ サイズにより、メッセージ送信時にクライアントからサーバーに対して開始されるリクエストの数が減り、メッセージ送信全体のスループットと遅延が向上します。
バッチパラメータは次のように説明されます。
パラメータ | 説明する |
---|---|
batch.size |
各パーティションに送信されたキャッシュされたメッセージの量 (メッセージの数ではなく、メッセージ コンテンツのバイトの合計)。設定値に達すると、ネットワーク リクエストがトリガーされ、プロデューサー クライアントはメッセージをバッチでサーバーに送信します。 |
linger.ms |
各メッセージがキャッシュ内に存在する最大時間。この時間を超えると、プロデューサー クライアントは制限batch.size を無視し、メッセージをサーバーにただちに送信します。 |
buffer.memory |
キャッシュされたすべてのメッセージの合計サイズがこの値を超えると、メッセージはサーバーに送信され、batch.size とlinger.ms 無視されます。buffer.memory のデフォルト値は 32MB で、単一のプロデューサーに対して十分なパフォーマンスを保証できます。 |
バッチ関連のパラメーター値を選択するための一般的な方法はありませんが、パフォーマンス重視のビジネス シナリオでは、圧力テストとチューニングを実行することをお勧めします。
スティッキー パーティションを使用して一括送信を処理する
Kafka プロデューサーとサーバーにはメッセージ送信時にバッチ送信メカニズムがあり、同じパーティションに送信されたメッセージのみが同じバッチに入れられます。大規模なバッチ送信シナリオでは、メッセージが複数のパーティションに分散されている場合、複数の小さなバッチが形成され、バッチ送信メカニズムが失敗してパフォーマンスが低下する可能性があります。
Kafka のパーティションを選択するためのデフォルトの戦略は次のとおりです。
シーン | ストラテジー |
---|---|
メッセージ指定キー | メッセージのキーをハッシュし、ハッシュ結果に基づいてパーティションを選択し、同じキーを持つメッセージが同じパーティションに送信されるようにします。 |
メッセージにキーが指定されていません | デフォルトの戦略では、トピックのすべてのパーティションを循環し、ラウンドロビン方式で各パーティションにメッセージを送信します。 |
デフォルトのメカニズムからわかるように、パーティションの選択は非常にランダムであるため、大量転送のシナリオでは、partitioner.class
パラメーターカスタム パーティション選択アルゴリズムを指定してスティッキー パーティションを実装することをお勧めします。
実装方法の 1 つは、データが複数の異なるパーティションに分散することを避けるために、一定期間同じパーティションを使用し、一定期間後に次のパーティションに切り替えるというものです。
一般的なベストプラクティス
Kafka によるメッセージ順序の保証
Kafka は同じパーティション内のメッセージの順序を保証しますが、トピック内に複数のパーティションがある場合、グローバルな順序は保証できません。グローバルな順序を保証する必要がある場合は、パーティションの数を 1 に制御する必要があります。
メッセージに一意のキーを設定します
メッセージ キュー Kafka のメッセージには、Key (メッセージ識別子) と Value (メッセージの内容) の 2 つのフィールドがあります。追跡を容易にするために、メッセージに一意のキーを設定することをお勧めします。その後、キーを介してメッセージを追跡し、送信ログと消費ログを出力し、メッセージの生成と消費を理解できるようになります。
キューの再試行戦略を合理的に設定する
分散環境では、ネットワークなどの理由により、メッセージは正常に送信されたものの、ACK メカニズムが失敗したり、メッセージが正常に送信されなかったりする場合があります。デフォルトのパラメータはほとんどのシナリオに対応できますが、ビジネス ニーズに応じて次の再試行パラメータを設定できます。
パラメータ | 説明する |
---|---|
retries |
再試行回数のデフォルト値は 3 ですが、データ損失に対する許容度がゼロのアプリケーションの場合は、Integer.MAX_VALUE (有効かつ最大) に設定することを検討してください。 |
retry.backoff.ms |
再試行間隔は 1000 に設定することをお勧めします。 |
:exclamation: 注意:
At Most Once セマンティクスを実装する場合は、再試行をオフにする必要があります。
ベストプラクティスにアクセスする
Spark Streaming を Kafka に接続する
Spark Streaming は、永続データの高スループットかつフォールトトレラントな処理を目的とした Spark Core の拡張機能であり、現在サポートされている外部入力には、Kafka、Flume、HDFS/S3、Kinesis、Twitter、TCP ソケットが含まれます。
Spark Streaming は連続データを DStream (Discretized Stream) に抽象化します。DStream は一連の連続した RDD (Elastic Distributed Data Set) で構成され、各 RDD は一定の時間間隔内に生成されるデータです。関数を使用して DStream を処理することは、実際にはこれらの RDD を処理することになります。
Spark Streaming を Kafka のデータ入力として使用する場合、Kafka の安定版と実験版をサポートできます。
カフカのバージョン | スパークストリーミングカフカ-0.8 | スパークストリーミングカフカ-0.10 |
---|---|---|
ブローカーのバージョン | 0.8.2.1以降 | 0.10.0以降 |
API の成熟度 | 廃止されました | 安定 |
言語サポート | スカラ、Java、Python | スカラ、Java |
受信機 DStream | はい | いいえ |
ダイレクト DStream | はい | はい |
SSL/TLSのサポート | いいえ | はい |
オフセットコミットAPI | いいえ | はい |
動的なトピックのサブスクリプション | いいえ | はい |
この実践では、バージョン 0.10.2.1 の Kafka 依存関係を使用します。
ステップ
ステップ 1: Kafka クラスターとトピックを作成する
Kafka クラスターを作成する手順は省略し、その後、test
という名前の。
ステップ 2: サーバー環境を準備する
Centos6.8系
パッケージ | バージョン |
---|---|
sbt | 0.13.16 |
ハドゥープ | 2.7.3 |
スパーク | 2.1.0 |
プロトブフ | 2.5.0 |
ssh | CentOSのデフォルトインストール |
ジャワ | 1.8 |
次の手順を含む、特定のインストール手順は省略されています。
- SBTをインストールする
- protobuf をインストールする
- Hadoopをインストールする
- スパークをインストールする
ステップ 3: Kafka に接続する
Kafka へのメッセージを生成する
ここでは、バージョン 0.10.2.1 の Kafka 依存関係が使用されます。
- 依存関係を以下に
build.sbt
追加します。
name := "Producer Example"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies += "org.apache.kafka" % "kafka-clients" % "0.10.2.1"
構成
producer_example.scala
:import java.util.Properties import org.apache.kafka.clients.producer._ object ProducerExample extends App { val props = new Properties() props.put("bootstrap.servers", "172.0.0.1:9092") //实例信息中的内网 IP 与端口 props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer") props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer") val producer = new KafkaProducer[String, String](props) val TOPIC="test" //指定要生产的 Topic for(i<- 1 to 50){ val record = new ProducerRecord(TOPIC, "key", s"hello $i") //生产 key 是"key",value 是 hello i 的消息 producer.send(record) } val record = new ProducerRecord(TOPIC, "key", "the end "+new java.util.Date) producer.send(record) producer.close() //最后要断开 }
ProducerRecord の詳しい使用方法については、ProducerRecord のドキュメントを参照してください。
Kafka からのメッセージを消費する
ダイレクトストリーム
- 依存関係を以下に
build.sbt
追加します。
name := "Consumer Example"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0"
libraryDependencies += "org.apache.spark" %% "spark-streaming" % "2.1.0"
libraryDependencies += "org.apache.spark" %% "spark-streaming-kafka-0-10" % "2.1.0"
- 構成
DirectStream_example.scala
:
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.kafka.common.TopicPartition
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.OffsetRange
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import collection.JavaConversions._
import Array._
object Kafka {
def main(args: Array[String]) {
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "172.0.0.1:9092",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "spark_stream_test1",
"auto.offset.reset" -> "earliest",
"enable.auto.commit" -> "false"
)
val sparkConf = new SparkConf()
sparkConf.setMaster("local")
sparkConf.setAppName("Kafka")
val ssc = new StreamingContext(sparkConf, Seconds(5))
val topics = Array("spark_test")
val offsets : Map[TopicPartition, Long] = Map()
for (i <- 0 until 3){
val tp = new TopicPartition("spark_test", i)
offsets.updated(tp , 0L)
}
val stream = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams)
)
println("directStream")
stream.foreachRDD{ rdd=>
//输出获得的消息
rdd.foreach{iter =>
val i = iter.value
println(s"${i}")
}
//获得offset
val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
rdd.foreachPartition { iter =>
val o: OffsetRange = offsetRanges(TaskContext.get.partitionId)
println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
}
}
// Start the computation
ssc.start()
ssc.awaitTermination()
}
}
RDD
- 構成
build.sbt
(構成は上記と同じです。クリックして表示します)。 - 構成
RDD_example
:
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.OffsetRange
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import collection.JavaConversions._
import Array._
object Kafka {
def main(args: Array[String]) {
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "172.0.0.1:9092",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "spark_stream",
"auto.offset.reset" -> "earliest",
"enable.auto.commit" -> (false: java.lang.Boolean)
)
val sc = new SparkContext("local", "Kafka", new SparkConf())
val java_kafkaParams : java.util.Map[String, Object] = kafkaParams
//按顺序向 parition 拉取相应 offset 范围的消息,如果拉取不到则阻塞直到超过等待时间或者新生产消息达到拉取的数量
val offsetRanges = Array[OffsetRange](
OffsetRange("spark_test", 0, 0, 5),
OffsetRange("spark_test", 1, 0, 5),
OffsetRange("spark_test", 2, 0, 5)
)
val range = KafkaUtils.createRDD[String, String](
sc,
java_kafkaParams,
offsetRanges,
PreferConsistent
)
range.foreach(rdd=>println(rdd.value))
sc.stop()
}
}
詳しいkafkaParams
使用方法、 kafkaParams のドキュメントを参照してください。
Flume を Kafka に接続する
Apache Flume 是一个分布式、可靠、高可用的日志收集系统,支持各种各样的数据来源(如 HTTP、Log 文件、JMS、监听端口数据等),能将这些数据源的海量日志数据进行高效收集、聚合、移动,最后存储到指定存储系统中(如 Kafka、分布式文件系统、Solr 搜索服务器等)。
Flume 基本结构如下:
Flume 以 agent 为最小的独立运行单位。一个 agent 就是一个 JVM,单个 agent 由 Source、Sink 和 Channel 三大组件构成。
Flume 与 Kafka
把数据存储到 HDFS 或者 HBase 等下游存储模块或者计算模块时需要考虑各种复杂的场景,例如并发写入的量以及系统承载压力、网络延迟等问题。Flume 作为灵活的分布式系统具有多种接口,同时提供可定制化的管道。 在生产处理环节中,当生产与处理速度不一致时,Kafka 可以充当缓存角色。Kafka 拥有 partition 结构以及采用 append 追加数据,使 Kafka 具有优秀的吞吐能力;同时其拥有 replication 结构,使 Kafka 具有很高的容错性。 所以将 Flume 和 Kafka 结合起来,可以满足生产环境中绝大多数要求。
准备工作
- 下载 Apache Flume (1.6.0以上版本兼容 Kafka)
- 下载 Kafka工具包 (0.9.x以上版本,0.8已经不支持)
- 确认 Kafka 的 Source、 Sink 组件已经在 Flume 中。
接入方式
Kafka 可作为 Source 或者 Sink 端对消息进行导入或者导出。
Kafka Source
配置 kafka 作为消息来源,即将自己作为消费者,从 Kafka 中拉取数据传入到指定 Sink 中。主要配置选项如下:
配置项 | 说明 |
---|---|
channels |
自己配置的 Channel |
type |
必须为:org.apache.flume.source.kafka.KafkaSource |
kafka.bootstrap.servers |
Kafka Broker 的服务器地址 |
kafka.consumer.group.id |
作为 Kafka 消费端的 Group ID |
kafka.topics |
Kafka 中数据来源 Topic |
batchSize |
每次写入 Channel 的大小 |
batchDurationMillis |
每次写入最大间隔时间 |
示例:
tier1.sources.source1.type = org.apache.flume.source.kafka.KafkaSource
tier1.sources.source1.channels = channel1
tier1.sources.source1.batchSize = 5000
tier1.sources.source1.batchDurationMillis = 2000
tier1.sources.source1.kafka.bootstrap.servers = localhost:9092
tier1.sources.source1.kafka.topics = test1, test2
tier1.sources.source1.kafka.consumer.group.id = custom.g.id
更多内容请参考 Apache Flume 官网。
Kafka Sink
配置 Kafka 作为内容接收方,即将自己作为生产者,推到 Kafka Server 中等待后续操作。主要配置选项如下:
配置项 | 说明 |
---|---|
channel |
自己配置的 Channel |
type |
必须为:org.apache.flume.sink.kafka.KafkaSink |
kafka.bootstrap.servers |
Kafka Broker 的服务器 |
kafka.topics |
Kafka 中数据来源 Topic |
kafka.flumeBatchSize |
每次写入的 Bacth 大小 |
kafka.producer.acks |
Kafka 生产者的生产策略 |
示例:
a1.sinks.k1.channel = c1
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.topic = mytopic
a1.sinks.k1.kafka.bootstrap.servers = localhost:9092
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
更多内容请参考 Apache Flume 官网。
Storm 接入 Kafka
Storm 是一个分布式实时计算框架,能够对数据进行流式处理和提供通用性分布式 RPC 调用,可以实现处理事件亚秒级的延迟,适用于对延迟要求比较高的实时数据处理场景。
Storm 工作原理
在 Storm 的集群中有两种节点,控制节点Master Node
和工作节点Worker Node
。Master Node
上运行Nimbus
进程,用于资源分配与状态监控。Worker Node
上运行Supervisor
进程,监听工作任务,启动executor
执行。整个 Storm 集群依赖zookeeper
负责公共数据存放、集群状态监听、任务分配等功能。
用户提交给 Storm 的数据处理程序称为topology
,它处理的最小消息单位是tuple
,一个任意对象的数组。topology
由spout
和bolt
构成,spout
是产生tuple
的源头,bolt
可以订阅任意spout
或bolt
发出的tuple
进行处理。
Storm with Kafka
Storm 可以把 Kafka 作为spout
,消费数据进行处理;也可以作为bolt
,存放经过处理后的数据提供给其它组件消费。
Centos6.8系统
package | version |
---|---|
maven | 3.5.0 |
storm | 2.1.0 |
ssh | 5.3 |
Java | 1.8 |
前提条件
- 下载并安装 JDK 8。具体操作,请参见 Download JDK 8。
- 下载并安装 Storm,参考 Apache Storm downloads。
- 已创建 Kafka 集群。
操作步骤
步骤1:创建 Topic
步骤2:添加 Maven 依赖
pom.xml 配置如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>storm</groupId>
<artifactId>storm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>storm</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-kafka-client</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>0.10.2.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>ExclamationTopology</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
步骤3:生产消息
使用 spout/bolt
topology 代码:
//TopologyKafkaProducerSpout.java
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.kafka.bolt.KafkaBolt;
import org.apache.storm.kafka.bolt.mapper.FieldNameBasedTupleToKafkaMapper;
import org.apache.storm.kafka.bolt.selector.DefaultTopicSelector;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.utils.Utils;
import java.util.Properties;
public class TopologyKafkaProducerSpout {
//申请的kafka实例ip:port
private final static String BOOTSTRAP_SERVERS = "xx.xx.xx.xx:xxxx";
//指定要将消息写入的topic
private final static String TOPIC = "storm_test";
public static void main(String[] args) throws Exception {
//设置producer属性
//函数参考:https://kafka.apache.org/0100/javadoc/index.html?org/apache/kafka/clients/consumer/KafkaConsumer.html
//属性参考:http://kafka.apache.org/0102/documentation.html
Properties properties = new Properties();
properties.put("bootstrap.servers", BOOTSTRAP_SERVERS);
properties.put("acks", "1");
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
//创建写入kafka的bolt,默认使用fields("key" "message")作为生产消息的key和message,也可以在FieldNameBasedTupleToKafkaMapper()中指定
KafkaBolt kafkaBolt = new KafkaBolt()
.withProducerProperties(properties)
.withTopicSelector(new DefaultTopicSelector(TOPIC))
.withTupleToKafkaMapper(new FieldNameBasedTupleToKafkaMapper());
TopologyBuilder builder = new TopologyBuilder();
//一个顺序生成消息的spout类,输出field是sentence
SerialSentenceSpout spout = new SerialSentenceSpout();
AddMessageKeyBolt bolt = new AddMessageKeyBolt();
builder.setSpout("kafka-spout", spout, 1);
//为tuple加上生产到kafka所需要的fields
builder.setBolt("add-key", bolt, 1).shuffleGrouping("kafka-spout");
//写入kafka
builder.setBolt("sendToKafka", kafkaBolt, 8).shuffleGrouping("add-key");
Config config = new Config();
if (args != null && args.length > 0) {
//集群模式,用于打包jar,并放到storm运行
config.setNumWorkers(1);
StormSubmitter.submitTopologyWithProgressBar(args[0], config, builder.createTopology());
} else {
//本地模式
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", config, builder.createTopology());
Utils.sleep(10000);
cluster.killTopology("test");
cluster.shutdown();
}
}
}
创建一个顺序生成消息的 spout 类:
import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.Map;
import java.util.UUID;
public class SerialSentenceSpout extends BaseRichSpout {
private SpoutOutputCollector spoutOutputCollector;
@Override
public void open(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector) {
this.spoutOutputCollector = spoutOutputCollector;
}
@Override
public void nextTuple() {
Utils.sleep(1000);
//生产一个UUID字符串发送给下一个组件
spoutOutputCollector.emit(new Values(UUID.randomUUID().toString()));
}
@Override
public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
outputFieldsDeclarer.declare(new Fields("sentence"));
}
}
为 tuple
加上 key、message 两个字段,当 key 为 null 时,生产的消息均匀分配到各个 partition,指定了 key 后将按照 key 值 hash 到特定 partition 上:
//AddMessageKeyBolt.java
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
public class AddMessageKeyBolt extends BaseBasicBolt {
@Override
public void execute(Tuple tuple, BasicOutputCollector basicOutputCollector) {
//取出第一个filed值
String messae = tuple.getString(0);
//System.out.println(messae);
//发送给下一个组件
basicOutputCollector.emit(new Values(null, messae));
}
@Override
public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
//创建发送给下一个组件的schema
outputFieldsDeclarer.declare(new Fields("key", "message"));
}
}
使用 trident
使用 trident 类生成 topology:
//TopologyKafkaProducerTrident.java
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.kafka.trident.TridentKafkaStateFactory;
import org.apache.storm.kafka.trident.TridentKafkaStateUpdater;
import org.apache.storm.kafka.trident.mapper.FieldNameBasedTupleToKafkaMapper;
import org.apache.storm.kafka.trident.selector.DefaultTopicSelector;
import org.apache.storm.trident.TridentTopology;
import org.apache.storm.trident.operation.BaseFunction;
import org.apache.storm.trident.operation.TridentCollector;
import org.apache.storm.trident.tuple.TridentTuple;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.Properties;
public class TopologyKafkaProducerTrident {
//申请的kafka实例ip:port
private final static String BOOTSTRAP_SERVERS = "xx.xx.xx.xx:xxxx";
//指定要将消息写入的topic
private final static String TOPIC = "storm_test";
public static void main(String[] args) throws Exception {
//设置producer属性
//函数参考:https://kafka.apache.org/0100/javadoc/index.html?org/apache/kafka/clients/consumer/KafkaConsumer.html
//属性参考:http://kafka.apache.org/0102/documentation.html
Properties properties = new Properties();
properties.put("bootstrap.servers", BOOTSTRAP_SERVERS);
properties.put("acks", "1");
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
//设置Trident
TridentKafkaStateFactory stateFactory = new TridentKafkaStateFactory()
.withProducerProperties(properties)
.withKafkaTopicSelector(new DefaultTopicSelector(TOPIC))
//设置使用fields("key", "value")作为消息写入 不像FieldNameBasedTupleToKafkaMapper有默认值
.withTridentTupleToKafkaMapper(new FieldNameBasedTupleToKafkaMapper("key", "value"));
TridentTopology builder = new TridentTopology();
//一个批量产生句子的spout,输出field为sentence
builder.newStream("kafka-spout", new TridentSerialSentenceSpout(5))
.each(new Fields("sentence"), new AddMessageKey(), new Fields("key", "value"))
.partitionPersist(stateFactory, new Fields("key", "value"), new TridentKafkaStateUpdater(), new Fields());
Config config = new Config();
if (args != null && args.length > 0) {
//集群模式,用于打包jar,并放到storm运行
config.setNumWorkers(1);
StormSubmitter.submitTopologyWithProgressBar(args[0], config, builder.build());
} else {
//本地模式
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", config, builder.build());
Utils.sleep(10000);
cluster.killTopology("test");
cluster.shutdown();
}
}
private static class AddMessageKey extends BaseFunction {
@Override
public void execute(TridentTuple tridentTuple, TridentCollector tridentCollector) {
//取出第一个filed值
String messae = tridentTuple.getString(0);
//System.out.println(messae);
//发送给下一个组件
//tridentCollector.emit(new Values(Integer.toString(messae.hashCode()), messae));
tridentCollector.emit(new Values(null, messae));
}
}
}
创建一个批量生成消息的 spout 类:
//TridentSerialSentenceSpout.java
import org.apache.storm.Config;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.trident.operation.TridentCollector;
import org.apache.storm.trident.spout.IBatchSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.Map;
import java.util.UUID;
public class TridentSerialSentenceSpout implements IBatchSpout {
private final int batchCount;
public TridentSerialSentenceSpout(int batchCount) {
this.batchCount = batchCount;
}
@Override
public void open(Map map, TopologyContext topologyContext) {
}
@Override
public void emitBatch(long l, TridentCollector tridentCollector) {
Utils.sleep(1000);
for(int i = 0; i < batchCount; i++){
tridentCollector.emit(new Values(UUID.randomUUID().toString()));
}
}
@Override
public void ack(long l) {
}
@Override
public void close() {
}
@Override
public Map<String, Object> getComponentConfiguration() {
Config conf = new Config();
conf.setMaxTaskParallelism(1);
return conf;
}
@Override
public Fields getOutputFields() {
return new Fields("sentence");
}
}
步骤4:消费消息
使用 spout/bolt
//TopologyKafkaConsumerSpout.java
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.kafka.spout.*;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.HashMap;
import java.util.Map;
import static org.apache.storm.kafka.spout.FirstPollOffsetStrategy.LATEST;
public class TopologyKafkaConsumerSpout {
//申请的kafka实例ip:port
private final static String BOOTSTRAP_SERVERS = "xx.xx.xx.xx:xxxx";
//指定要将消息写入的topic
private final static String TOPIC = "storm_test";
public static void main(String[] args) throws Exception {
//设置重试策略
KafkaSpoutRetryService kafkaSpoutRetryService = new KafkaSpoutRetryExponentialBackoff(
KafkaSpoutRetryExponentialBackoff.TimeInterval.microSeconds(500),
KafkaSpoutRetryExponentialBackoff.TimeInterval.milliSeconds(2),
Integer.MAX_VALUE,
KafkaSpoutRetryExponentialBackoff.TimeInterval.seconds(10)
);
ByTopicRecordTranslator<String, String> trans = new ByTopicRecordTranslator<>(
(r) -> new Values(r.topic(), r.partition(), r.offset(), r.key(), r.value()),
new Fields("topic", "partition", "offset", "key", "value"));
//设置consumer参数
//函数参考http://storm.apache.org/releases/1.1.0/javadocs/org/apache/storm/kafka/spout/KafkaSpoutConfig.Builder.html
//参数参考http://kafka.apache.org/0102/documentation.html
KafkaSpoutConfig spoutConfig = KafkaSpoutConfig.builder(BOOTSTRAP_SERVERS, TOPIC)
.setProp(new HashMap<String, Object>(){
{
put(ConsumerConfig.GROUP_ID_CONFIG, "test-group1"); //设置group
put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "50000"); //设置session超时
put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "60000"); //设置请求超时
}})
.setOffsetCommitPeriodMs(10_000) //设置自动确认时间
.setFirstPollOffsetStrategy(LATEST) //设置拉取最新消息
.setRetry(kafkaSpoutRetryService)
.setRecordTranslator(trans)
.build();
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("kafka-spout", new KafkaSpout(spoutConfig), 1);
builder.setBolt("bolt", new BaseRichBolt(){
private OutputCollector outputCollector;
@Override
public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
}
@Override
public void prepare(Map map, TopologyContext topologyContext, OutputCollector outputCollector) {
this.outputCollector = outputCollector;
}
@Override
public void execute(Tuple tuple) {
System.out.println(tuple.getStringByField("value"));
outputCollector.ack(tuple);
}
}, 1).shuffleGrouping("kafka-spout");
Config config = new Config();
config.setMaxSpoutPending(20);
if (args != null && args.length > 0) {
config.setNumWorkers(3);
StormSubmitter.submitTopologyWithProgressBar(args[0], config, builder.createTopology());
}
else {
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", config, builder.createTopology());
Utils.sleep(20000);
cluster.killTopology("test");
cluster.shutdown();
}
}
}
使用 trident
//TopologyKafkaConsumerTrident.java
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.generated.StormTopology;
import org.apache.storm.kafka.spout.ByTopicRecordTranslator;
import org.apache.storm.kafka.spout.trident.KafkaTridentSpoutConfig;
import org.apache.storm.kafka.spout.trident.KafkaTridentSpoutOpaque;
import org.apache.storm.trident.Stream;
import org.apache.storm.trident.TridentTopology;
import org.apache.storm.trident.operation.BaseFunction;
import org.apache.storm.trident.operation.TridentCollector;
import org.apache.storm.trident.tuple.TridentTuple;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.HashMap;
import static org.apache.storm.kafka.spout.FirstPollOffsetStrategy.LATEST;
public class TopologyKafkaConsumerTrident {
//申请的kafka实例ip:port
private final static String BOOTSTRAP_SERVERS = "xx.xx.xx.xx:xxxx";
//指定要将消息写入的topic
private final static String TOPIC = "storm_test";
public static void main(String[] args) throws Exception {
ByTopicRecordTranslator<String, String> trans = new ByTopicRecordTranslator<>(
(r) -> new Values(r.topic(), r.partition(), r.offset(), r.key(), r.value()),
new Fields("topic", "partition", "offset", "key", "value"));
//设置consumer参数
//函数参考http://storm.apache.org/releases/1.1.0/javadocs/org/apache/storm/kafka/spout/KafkaSpoutConfig.Builder.html
//参数参考http://kafka.apache.org/0102/documentation.html
KafkaTridentSpoutConfig spoutConfig = KafkaTridentSpoutConfig.builder(BOOTSTRAP_SERVERS, TOPIC)
.setProp(new HashMap<String, Object>(){
{
put(ConsumerConfig.GROUP_ID_CONFIG, "test-group1"); //设置group
put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true"); //设置自动确认
put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "50000"); //设置session超时
put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "60000"); //设置请求超时
}})
.setFirstPollOffsetStrategy(LATEST) //设置拉取最新消息
.setRecordTranslator(trans)
.build();
TridentTopology builder = new TridentTopology();
// Stream spoutStream = builder.newStream("spout", new KafkaTridentSpoutTransactional(spoutConfig)); //事务型
Stream spoutStream = builder.newStream("spout", new KafkaTridentSpoutOpaque(spoutConfig));
spoutStream.each(spoutStream.getOutputFields(), new BaseFunction(){
@Override
public void execute(TridentTuple tridentTuple, TridentCollector tridentCollector) {
System.out.println(tridentTuple.getStringByField("value"));
tridentCollector.emit(new Values(tridentTuple.getStringByField("value")));
}
}, new Fields("message"));
Config conf = new Config();
conf.setMaxSpoutPending(20);conf.setNumWorkers(1);
if (args != null && args.length > 0) {
conf.setNumWorkers(3);
StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.build());
}
else {
StormTopology stormTopology = builder.build();
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", conf, stormTopology);
Utils.sleep(10000);
cluster.killTopology("test");
cluster.shutdown();stormTopology.clear();
}
}
}
步骤5:提交 Storm
使用 mvn package
编译后,可以提交到本地集群进行 debug 测试,也可以提交到正式集群进行运行。
storm jar your_jar_name.jar topology_name
storm jar your_jar_name.jar topology_name tast_name
Logstash 接入 Kafka
Logstash 是一个开源的日志处理工具,可以从多个源头收集数据、过滤收集的数据并对数据进行存储作为其他用途。
Logstash 灵活性强,拥有强大的语法分析功能,插件丰富,支持多种输入和输出源。Logstash 作为水平可伸缩的数据管道,与 Elasticsearch 和 Kibana 配合,在日志收集检索方面功能强大。
Logstash 工作原理
Logstash 数据处理可以分为三个阶段:inputs → filters → outputs。
- inputs:产生数据来源,例如文件、syslog、redis 和 beats 此类来源。
- filters:修改过滤数据, 在 Logstash 数据管道中属于中间环节,可以根据条件去对事件进行更改。一些常见的过滤器包括:grok、mutate、drop 和 clone 等。
- outputs:将数据传输到其他地方,一个事件可以传输到多个 outputs,当传输完成后这个事件就结束。Elasticsearch 就是最常见的 outputs。
同时 Logstash 支持编码解码,可以在 inputs 和 outputs 端指定格式。
Logstash 接入 Kafka 的优势
- 可以异步处理数据:防止突发流量。
- 解耦:当 Elasticsearch 异常的时候不会影响上游工作。
:exclamation: 注意: Logstash 过滤消耗资源,如果部署在生产 server 上会影响其性能。
操作步骤
准备工作
- 下载并安装 Logstash,参考 Download Logstash。
- 下载并安装 JDK 8,参考 Download JDK 8。
- 已创建 Kafka 集群。
步骤1:创建 Topic
创建一个名为 logstash_test
的 Topic。
步骤2:接入 Kafka
作为 inputs 接入
执行
bin/logstash-plugin list
,查看已经支持的插件是否含有logstash-input-kafka
。在
.bin/
目录下编写配置文件input.conf
。 此处将标准输出作为数据终点,将 Kafka 作为数据来源。input { kafka { bootstrap_servers => "xx.xx.xx.xx:xxxx" // kafka 实例接入地址 group_id => "logstash_group" // kafka groupid 名称 topics => ["logstash_test"] // kafka topic 名称 consumer_threads => 3 // 消费线程数,一般与 kafka 分区数一致 auto_offset_reset => "earliest" } } output { stdout{codec=>rubydebug} }
执行以下命令启动 Logstash,进行消息消费。
./logstash -f input.conf
会看到刚才 Topic 中的数据被消费出来。
作为 outputs 接入
执行
bin/logstash-plugin list
,查看已经支持的插件是否含有logstash-output-kafka
。在.
bin/
目录下编写配置文件output.conf
。 此处将标准输入作为数据来源,将 Kafka 作为数据目的地。input { input { stdin{} } } output { kafka { bootstrap_servers => "xx.xx.xx.xx:xxxx" // ckafka 实例接入地址 topic_id => "logstash_test" // ckafka topic 名称 } }
执行如下命令启动 Logstash,向创建的 Topic 发送消息。
./logstash -f output.conf
启动 Kafka 消费者,检验上一步的生产数据。
./kafka-console-consumer.sh --bootstrap-server 172.0.0.1:9092 --topic logstash_test --from-begging --new-consumer
Filebeats 接入 Kafka
Beats 平台 集合了多种单一用途数据采集器。这些采集器安装后可用作轻量型代理,从成百上千或成千上万台机器向目标发送采集数据。 Beats 有多种采集器,您可以根据自身的需求下载对应的采集器。本文以 Filebeat(轻量型日志采集器)为例,向您介绍 Filebeat 接入 Kafka 的操作指方法,及接入后常见问题的解决方法。
前提条件
- 下载并安装 Filebeat(参见 Download Filebeat)
- 下载并安装JDK 8(参见 Download JDK 8)
- 已 创建 Kafka 集群
操作步骤
步骤1:创建 Topic
创建一个名为 test
的 Topic。
步骤2:准备配置文件
进入 Filebeat 的安装目录,创建配置监控文件 filebeat.yml。
#======= Filebeat prospectors ==========
filebeat.prospectors:
- input_type: log
# 此处为监听文件路径
paths:
- /var/log/messages
#======= Outputs =========
#------------------ kafka -------------------------------------
output.kafka:
version:0.10.2 // 根据不同 Kafka 集群版本配置
# 设置为Kafka实例的接入地址
hosts: ["xx.xx.xx.xx:xxxx"]
# 设置目标topic的名称
topic: 'test'
partition.round_robin:
reachable_only: false
required_acks: 1
compression: none
max_message_bytes: 1000000
# SASL 需要配置下列信息,如果不需要则下面两个选项可不配置
username: "yourinstance#yourusername" //username 需要拼接实例ID和用户名
password: "yourpassword"
步骤4:Filebeat 发送消息
执行如下命令启动客户端。
sudo ./filebeat -e -c filebeat.yml
为监控文件增加数据(示例为写入监听的 testlog 文件)。
echo ckafka1 >> testlog echo ckafka2 >> testlog echo ckafka3 >> testlog
开启 Consumer 消费对应的 Topic,获得以下数据。
{"@timestamp":"2017-09-29T10:01:27.936Z","beat":{"hostname":"10.193.9.26","name":"10.193.9.26","version":"5.6.2"},"input_type":"log","message":"ckafka1","offset":500,"source":"/data/ryanyyang/hcmq/beats/filebeat-5.6.2-linux-x86_64/testlog","type":"log"} {"@timestamp":"2017-09-29T10:01:30.936Z","beat":{"hostname":"10.193.9.26","name":"10.193.9.26","version":"5.6.2"},"input_type":"log","message":"ckafka2","offset":508,"source":"/data/ryanyyang/hcmq/beats/filebeat-5.6.2-linux-x86_64/testlog","type":"log"} {"@timestamp":"2017-09-29T10:01:33.937Z","beat":{"hostname":"10.193.9.26","name":"10.193.9.26","version":"5.6.2"},"input_type":"log","message":"ckafka3","offset":516,"source":"/data/ryanyyang/hcmq/beats/filebeat-5.6.2-linux-x86_64/testlog","type":"log"}
SASL/PLAINTEXT 模式
如果您需要进行 SALS/PLAINTEXT 配置,则需要配置用户名与密码。 在 Kafka 配置区域新增加 username 和 password 配置即可。
参考链接
消息队列 CKafka - 文档中心 - 腾讯云 (tencent.com)
三人が一緒に歩く、必ず私の先生がいる、知識を共有する、世界は公共のものであるこの記事は、Dongfeng Weiming 技術ブログEWhisper.cnによって書かれました。