Structured Streaming教程(3) —— 與Kafka的集成

Structured Streaming最主要的生產環境應用場景就是配合kafka做實時處理,不過在Strucured Streaming中kafka的版本要求相對搞一些,只支持0.10及以上的版本。就在前一個月,我們才從0.9升級到0.10,終於可以嘗試structured streaming的很多用法,很開心~

引入

如果是maven工程,直接添加對應的kafka的jar包即可:

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-sql-kafka-0-10_2.11</artifactId>
    <version>2.2.0</version>
</dependency>

讀取kafka的數據

 


在這裏還是要推薦下我自己建的大數據學習羣:199427210,羣裏都是學大數據開發的,如果你正在學習大數據 ,小編歡迎你加入,大家都是軟件開發黨,不定期分享乾貨(只有大數據軟件開發相關的),包括我自己整理的一份最新的大數據進階資料和高級開發教程,歡迎進階中和進想深入大數據的小夥伴加入。

以流的形式查詢

讀取的時候,可以讀取某個topic,也可以讀取多個topic,還可以指定topic的通配符形式:

讀取一個topic

val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribe", "topic1")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

讀取多個topic

val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribe", "topic1,topic2")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

讀取通配符形式的topic組

val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribePattern", "topic.*")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

以批的形式查詢

關於Kafka的offset,structured streaming默認提供了幾種方式:

設置每個分區的起始和結束值

val df = spark
  .read
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribe", "topic1,topic2")
  .option("startingOffsets", """{"topic1":{"0":23,"1":-2},"topic2":{"0":-2}}""")
  .option("endingOffsets", """{"topic1":{"0":50,"1":-1},"topic2":{"0":-1}}""")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

配置起始和結束的offset值(默認)

val df = spark
  .read
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribePattern", "topic.*")
  .option("startingOffsets", "earliest")
  .option("endingOffsets", "latest")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

Schema信息

讀取後的數據的Schema是固定的,包含的列如下:

Column Type 說明
key binary 信息的key
value binary 信息的value(我們自己的數據)
topic string 主題
partition int 分區
offset long 偏移值
timestamp long 時間戳
timestampType int 類型

source相關的配置

無論是流的形式,還是批的形式,都需要一些必要的參數:

  • kafka.bootstrap.servers kafka的服務器配置,host:post形式,用逗號進行分割,如host1:9000,host2:9000
  • assign,以json的形式指定topic信息
  • subscribe,通過逗號分隔,指定topic信息
  • subscribePattern,通過java的正則指定多個topic
    assign、subscribe、subscribePattern同時之中能使用一個。

其他比較重要的參數有:

  • startingOffsets, offset開始的值,如果是earliest,則從最早的數據開始讀;如果是latest,則從最新的數據開始讀。默認流是latest,批是earliest
  • endingOffsets,最大的offset,只在批處理的時候設置,如果是latest則爲最新的數據
  • failOnDataLoss,在流處理時,當數據丟失時(比如topic被刪除了,offset在指定的範圍之外),查詢是否報錯,默認爲true。這個功能可以當做是一種告警機制,如果對丟失數據不感興趣,可以設置爲false。在批處理時,這個值總是爲true。
  • kafkaConsumer.pollTimeoutMs,excutor連接kafka的超時時間,默認是512ms
  • fetchOffset.numRetries,獲取kafka的offset信息時,嘗試的次數;默認是3次
  • fetchOffset.retryIntervalMs,嘗試重新讀取kafka offset信息時等待的時間,默認是10ms
  • maxOffsetsPerTrigger,trigger暫時不會用,不太明白什麼意思。Rate limit on maximum number of offsets processed per trigger interval. The specified total number of offsets will be proportionally split across topicPartitions of different volume.

寫入數據到Kafka

Apache kafka僅支持“至少一次”的語義,因此,無論是流處理還是批處理,數據都有可能重複。比如,當出現失敗的時候,structured streaming會嘗試重試,但是不會確定broker那端是否已經處理以及持久化該數據。但是如果query成功,那麼可以斷定的是,數據至少寫入了一次。比較常見的做法是,在後續處理kafka數據時,再進行額外的去重,關於這點,其實structured streaming有專門的解決方案。

保存數據時的schema:

  • key,可選。如果沒有填,那麼key會當做null,kafka針對null會有專門的處理(待查)。
  • value,必須有
  • topic,可選。(如果配置option裏面有topic會覆蓋這個字段)

下面是sink輸出必須要有的參數:

  • kafka.bootstrap.servers,kafka的集羣地址,host:port格式用逗號分隔。

流處理的數據寫入

// 基於配置指定topic
val ds = df
  .selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("topic", "topic1")
  .start()

// 在字段中包含topic
val ds = df
  .selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .start()

批處理的數據寫入

跟流處理其實一樣

df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .write
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("topic", "topic1")
  .save()

df.selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
  .write
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .save()

kafka的特殊配置

針對Kafka的特殊處理,可以通過DataStreamReader.option進行設置。

 

注意下面的參數是不能被設置的,否則kafka會拋出異常:

  • group.id kafka的source會在每次query的時候自定創建唯一的group id
  • auto.offset.reset 爲了避免每次手動設置startingoffsets的值,structured streaming在內部消費時會自動管理offset。這樣就能保證訂閱動態的topic時不會丟失數據。startingOffsets在流處理時,只會作用於第一次啓動時,之後的處理都會自定的讀取保存的offset。
  • key.deserializer,value.deserializer,key.serializer,value.serializer 序列化與反序列化,都是ByteArraySerializer
  • enable.auto.commit kafka的source不會提交任何的offset
  • interceptor.classes 由於kafka source讀取數據都是二進制的數組,因此不能使用任何攔截器進行處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章