流計算中kafka的OffsetReset策略

朋友的公司做的是西南某邊境省份網紅新能源車的數據處理,由於新能源車的火爆,從年初從現在,數據量已經翻番。但與此同時,服務器卻沒有多少增加。無奈之下,只能暫時將kafka的數據存儲時間由之前的1天改爲6小時,5小時。其實有點危險,如果在凌晨發生什麼異常生產事故導致實時任務退出,如果運維沒有及時通知,導致第二天上班時已經超過5小時,那麼這部份數據可能就丟了。但這也是無奈之舉。

本來運行一段時間倒也好好的。今天早上,突然在微信上問我一個問題。

image.png

一時間有點懵,我很確信這個問題我肯定遇到過,且解決過。但我一時之間還真沒馬上想起來解決方案。

我立馬翻出以前寫的 Structured Streaming代碼,馬上意識到,這不就是auto.offset.reset的問題嗎?在異常信息裏已經提示得很明確了。
爲什麼我會第一時間沒反應過來呢?因爲不管是spark streaming,spark Structured Streaming,flink實時任務,只要涉及到kafka作爲數據源,配置auto.offset.reset已經是一個標配。無腦上這個配置。反而一時間想不起來了。

朋友公司的代碼可以追溯到在大數據領域比較遙遠的2016年,那是spark1.0的時代,採用spark streaming,而現在spark官方早都已經不維護spark streaming模塊了。當時的基礎代碼一直被複制沿用,改的也只是業務代碼。所以auto.offset.reset這個配置就一直被忽略了。正常情況下,也不會拋出問題,但就怕特殊情況。

這裏做一個記錄。
簡單說auto.offset.reset配置在沒有獲取到group offset的時候(比如第一次讀取,或者數據被刪除,就像朋友發的情況,因爲kafka數據過期被刪除或者異常手動被刪等等),有一個替代策略。
根據不同的大數據引擎它一般有3個配置值 。

LATEST, 從最新的開始讀
EARLIEST,從最晚的開始讀
NONE 默認

也有group offset直接設置具體值的。
根據kafka的
官方文檔的表述。

What to do when there is no initial offset in Kafka or if the current offset does not exist any more on the server (e.g. because that data has been deleted):

-   earliest: automatically reset the offset to the earliest offset
-   latest: automatically reset the offset to the latest offset
-   none: throw exception to the consumer if no previous offset is found for the consumer's group
-   anything else: throw exception to the consumer.

| Type:         | string                   |
| ------------- | ------------------------ |
| Default:      | latest                   |
| Valid Values: | [latest, earliest, none] |
| Importance:   | medium

一點疑惑。
在spark官方jira看到一些相當的異常描述,比如這哥們,說它設置了相關的配置,但是還是出現了此種異常。

e7097e3c356084946b391e5d3296c6d.jpg
朋友也有此方面的疑惑,他認爲或許auto.offset.reset配置只針對初始化,但在kafka分區或者分區數據被刪除的情況下,此配置並不生效。

我記得在去年做過此方面的測試。一個測試kafka topic,數據失效時間設置爲2天,先消費一波,等過3天,再去生產數據並消費,並沒有出現異常。

今天正好再次做一次試驗,手頭剛好有flink代碼環境。就以flink 1.14 + kafka 2.x爲例。

示例代碼

"CREATE TABLE test (\n"
        + "  `key` STRING,\n"
        + "  `time` TIMESTAMP(3),\n"
        + "  `price` float,\n"
        + "  WATERMARK FOR `time` AS `time` - INTERVAL '10' SECOND"
        + ") WITH (\n"
        + "  'connector' = 'kafka',\n"
        + "  'topic' = 'test2',\n"
        + "  'properties.bootstrap.servers' = '192.168.124.122:9092',\n"
        + "  'properties.group.id' = 'windowGroup',\n"
        + "  'scan.startup.mode' = 'latest-offset',\n"
        + "  'format' = 'json',\n"
        + "  'json.fail-on-missing-field' = 'false',\n"
        + "  'json.ignore-parse-errors' = 'true' \n"
        + ")"

flink在table api的kakfa connector中,auto.offset.reset的配置變爲scan.startup.mode,看這表述,確實感覺有點誤會,像是隻是啓動初始化的時候生效。

先把kafka分區刪除一個,代碼走一波。

430bc76f81d142addaaee6372eb48c5.jpg

後臺並沒有異常錯誤及退出。可以看到,最先我刪除了index和log,然後發送了數據,kafka生成了新的inde和log,並且flink程序正常消費。

然後我把代碼中scan.startup.mode配置刪了。然後再把kafka中一個分區的index和log刪掉。啓動。然後報錯了。
跟spark的異常信息相似。
Undefined offset with no reset policy for partitions: [test2-0]

可見auto.offset.reset/scan.startup.mode跟官網和我之前預期是相符的。沒有問題。

簡單看一下flink 1.14相關源碼,關鍵位置在

SubscriptionState.resetMissingPositions

image.png

可是以看到,如果獲取不到Partation相關信息且沒有設置scan.startup.mode,那麼OffsetResetStrategy就是默認的NONE,這種情況下就會加入partitionsWithNoOffsets,然後當partitionsWithNoOffsets不爲空的時候,就會拋出異常。

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