Spark Streaming + Kafka 構造指南(Kafka broker version 0.10.0 or higher)

Table of Contents

依賴

創建一個直接流

偏移量策略

消費者的策略

Creating an RDD

獲得offsets

存儲offsets

Checkpoints

Kafka itself

Your own data store

SSL / TLS

部署


Kafka 0.10 的 Spark 流集成在設計上類似於0.8直接流方法。它提供了簡單的並行性,Kafka 分區和 Spark 分區之間的1:1對應,以及對偏移量和元數據的訪問。但是,因爲新的集成使用新的 Kafka 消費者 API 而不是簡單的 API,所以在使用上有顯著的不同。這個版本的集成被標記爲實驗性的,所以API可能會發生變化。

依賴

對於使用 SBT/Maven 項目定義的 Scala/Java 應用程序,將您的流應用程序鏈接到以下工件

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-10_2.11
version = 2.1.1

創建一個直接流

注意,導入的名稱空間包括版本 org.apache.spark.stream .kafka010

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

val kafkaParams = Map[String, Object](
  "bootstrap.servers" -> "localhost:9092,anotherhost:9092",
  "key.deserializer" -> classOf[StringDeserializer],
  "value.deserializer" -> classOf[StringDeserializer],
  "group.id" -> "use_a_separate_group_id_for_each_stream",
  "auto.offset.reset" -> "latest",
  "enable.auto.commit" -> (false: java.lang.Boolean)
)

val topics = Array("topicA", "topicB")
val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  PreferConsistent,
  Subscribe[String, String](topics, kafkaParams)
)

stream.map(record => (record.key, record.value))

流中的每一條都是一個 ConsumerRecord

有關 kafkaParams, see Kafka consumer config docs。如果您的 Spark 批處理持續時間大於默認的 Kafka 心跳會話超時(30秒),請增加 heartbeat.interval.ms and session.timeout.ms。對於大於5分鐘的批,這將需要更改 kafka broker 的 group.max.session.timeout.ms。注意,該示例將 enable.auto.commit 設置爲false,有關討論請參閱下面的存儲偏移量。

偏移量策略

新的 Kafka 消費者 API 將預取消息到緩衝區。因此,出於性能方面的考慮,Spark 集成將緩存的使用者保留在執行器中(而不是爲每個批處理重新創建它們),並且更喜歡將分區安排在擁有適當使用者的主機位置上,這一點非常重要。

在大多數情況下,你應該使用定位策略。如上面所示,首選一致性。這將在可用的執行者之間均勻地分配分區。如果您的執行者與 Kafka 代理位於相同的主機上,那麼使用 PreferBrokers,它更願意爲該分區在 Kafka leader 上調度分區。最後,如果分區之間的負載有明顯的傾斜,那麼使用 PreferFixed。這允許您指定分區到主機的顯式映射(任何未指定的分區將使用一致的位置)。

使用者緩存的默認最大大小爲64。如果您希望處理多於(64 *執行器數量)Kafka分區,您可以通過 spark.streaming.kafka.consumer.cache.maxCapacity 來更改該設置。

緩存由 topic partition和 group.id 鍵控。所以每個 createDirectStream 的調用使用一個單獨的 group.id。

消費者的策略

新的 Kafka 消費者 API 有許多不同的方式來指定主題,其中一些需要大量的對象實例化後的設置。客戶策略提供了一個抽象,允許 Spark 即使在從檢查點重新啓動之後也可以獲得正確配置的客戶。

如上所示,ConsumerStrategies.Subscribe 允許您訂閱固定的主題集合。SubscribePattern 允許您使用正則表達式來指定主題。注意,與0.8集成不同,使用 Subscribe 或 SubscribePattern 應該在運行流期間響應添加分區。最後,Assign 允許您指定一個固定的分區集合。這三種策略都有重載的構造函數,允許您指定特定分區的起始偏移量。

如果您有特定的消費者設置需求,而上面的選項不能滿足這些需求,那麼 ConsumerStrategy 是一個可以擴展的公共類。

Creating an RDD

如果您有一個更適合批處理的用例,那麼您可以爲定義的偏移範圍創建一個RDD。

// Import dependencies and create kafka params as in Create Direct Stream above

val offsetRanges = Array(
  // topic, partition, inclusive starting offset, exclusive ending offset
  OffsetRange("test", 0, 0, 100),
  OffsetRange("test", 1, 0, 100)
)

val rdd = KafkaUtils.createRDD[String, String](sparkContext, kafkaParams, offsetRanges, PreferConsistent)

請注意,您不能使用 preferbroker,因爲沒有流,就沒有一個驅動端使用者爲您自動查找代理元數據。如果需要,可以在自己的元數據查找中使用 PreferFixed。

獲得offsets

stream.foreachRDD { rdd =>
  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}")
  }
}

注意,對 HasOffsetRanges 的類型轉換隻有在對 createDirectStream 的結果調用的第一個方法中完成時纔會成功,而不是在隨後的方法鏈中.

請注意,RDD 分區和 Kafka 分區之間的一對一映射不會在任何洗牌或重分區的方法(例如reduceByKey()或window())之後仍然存在。

存儲offsets

失敗情況下的 Kafka 交付語義取決於如何以及何時存儲偏移量。spark 輸出操作至少一次。因此,如果您想要完全的一次語義,您必須在冪等的輸出之後存儲偏移量,或者將偏移量與輸出一起存儲在原子事務中。有了這個集成,您有3個選項,爲了增加可靠性(和代碼複雜性),來存儲偏移量。

Checkpoints

如果啓用了spark檢查點,則偏移量將存儲在檢查點中。這很容易實現,但也有缺點。你的輸出操作必須是冪等的,因爲你會得到重複的輸出;交易不是一個選項。此外,如果應用程序代碼發生了更改,則無法從檢查點恢復。對於計劃中的升級,您可以通過在運行舊代碼的同時運行新代碼來緩解這種情況(因爲無論如何輸出都需要是冪等的,它們不應該衝突)。但是對於需要更改代碼的計劃外故障,除非您有其他方法來識別已知的良好起始偏移量,否則您將丟失數據。

Kafka itself

Kafka 有一個偏移量提交 API,它將偏移量存儲在一個特殊的 Kafka 主題中。默認情況下,新使用者將定期自動提交補償。這幾乎肯定不是您想要的,因爲由使用者成功輪詢的消息可能尚未導致 Spark 輸出操作,從而導致語義未定義。

這就是爲什麼上面的流示例將“enable.auto.commit”設置爲false。但是,您可以使用 commitAsync API 在知道您的輸出已被存儲後將偏移量提交給 Kafka。與檢查點相比,Kafka 的優點是無論應用程序代碼如何更改,它都是一個持久的存儲。然而,Kafka 不是事務性的,所以你的輸出必須是冪等的。

stream.foreachRDD { rdd =>
  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges

  // some time later, after outputs have completed
  stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
}

與 HasOffsetRanges 一樣,只有在 createDirectStream 的結果上調用時,轉換到 CanCommitOffsets 纔會成功,而不是在轉換之後。commitAsync  調用是線程安全的,但如果需要有意義的語義,則必須在輸出之後執行。

Your own data store

對於支持事務的數據存儲,將偏移量保存在與結果相同的事務中可以使兩者保持同步,即使在出現故障的情況下也是如此。如果您小心地檢測重複或跳過的偏移範圍,回滾事務可以防止重複或丟失的消息影響結果。

這就給出了完全一次的語義。甚至對於聚合產生的輸出也可以使用這種策略,因爲聚合通常很難實現冪等性。

// The details depend on your data store, but the general idea looks like this

// begin from the the offsets committed to the database
val fromOffsets = selectOffsetsFromYourDatabase.map { resultSet =>
  new TopicPartition(resultSet.string("topic"), resultSet.int("partition")) -> resultSet.long("offset")
}.toMap

val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  PreferConsistent,
  Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
)

stream.foreachRDD { rdd =>
  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges

  val results = yourCalculation(rdd)

  // begin your transaction

  // update results
  // update offsets where the end of existing offsets matches the beginning of this batch of offsets
  // assert that offsets were updated correctly

  // end your transaction
}

SSL / TLS

新的 Kafka 使用者支持 SSL。要啓用它,請在傳遞到 createDirectStream / createRDD 之前適當地設置 kafkaParams。

注意,這隻適用於 Spark 和 Kafka 代理之間的通信;您仍然需要單獨負責保護 Spark 節點間通信。

val kafkaParams = Map[String, Object](
  // the usual params, make sure to change the port in bootstrap.servers if 9092 is not TLS
  "security.protocol" -> "SSL",
  "ssl.truststore.location" -> "/some-directory/kafka.client.truststore.jks",
  "ssl.truststore.password" -> "test1234",
  "ssl.keystore.location" -> "/some-directory/kafka.client.keystore.jks",
  "ssl.keystore.password" -> "test1234",
  "ssl.key.password" -> "test1234"
)

部署

與任何 Spark 應用程序一樣,Spark -submit 用於啓動應用程序。

對於 Scala 和 Java 應用程序,如果您使用SBT或Maven進行項目管理,那麼可以將spark-streaming-kafka-0-10_2.11及其依賴項打包到應用程序JAR中。確保Spark -core_2.11和Spark -streaming_2.11被標記爲提供的依賴項,就像它們在Spark安裝中已經存在一樣。然後使用spark-submit啓動您的應用程序

 

原文地址:http://spark.apache.org/docs/2.1.1/streaming-kafka-0-10-integration.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章