Table of Contents
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