Kafka聯結器深度解讀之錯誤處理和死信佇列

Java架構技術棧發表於2019-04-10

Kafka聯結器是Kafka的一部分,是在Kafka和其它技術之間構建流式管道的一個強有力的框架。它可用於將資料從多個地方(包括資料庫、訊息佇列和文字檔案)流式注入到Kafka,以及從Kafka將資料流式傳輸到目標端(如文件儲存、NoSQL、資料庫、物件儲存等)中。

現實世界並不完美,出錯是難免的,因此在出錯時Kafka的管道能儘可能優雅地處理是最好的。一個常見的場景是獲取與特定序列化格式不匹配的主題的訊息(比如預期為Avro時實際為JSON,反之亦然)。自從Kafka 2.0版本釋出以來,Kafka聯結器包含了錯誤處理選項,即將訊息路由到

死信佇列
的功能,這是構建資料管道的常用技術。

在本文中將介紹幾種處理問題的常見模式,並說明如何實現。

失敗後立即停止

有時可能希望在發生錯誤時立即停止處理,可能遇到質量差的資料是由於上游的原因導致的,必須由上游來解決,繼續嘗試處理其它的訊息已經沒有意義。

Kafka聯結器深度解讀之錯誤處理和死信佇列

這是Kafka聯結器的預設行為,也可以使用下面的配置項顯式地指定:

errors.tolerance = none
複製程式碼

在本示例中,該聯結器配置為從主題中讀取JSON格式資料,然後將其寫入純文字檔案。注意這裡為了演示使用的是FileStreamSinkConnector聯結器,不建議在生產中使用。

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_01",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_json",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "file":"/data/file_sink_01.txt"
                }
        }'
複製程式碼

主題中的某些JSON格式訊息是無效的,聯結器會立即終止,進入以下的FAILED狀態:

$ curl -s "http://localhost:8083/connectors/file_sink_01/status"| \
    jq -c -M '[.name,.tasks[].state]'
["file_sink_01","FAILED"]
複製程式碼

檢視Kafka聯結器工作節點的日誌,可以看到錯誤已經記錄並且任務已經終止:

org.apache.kafka.connect.errors.ConnectException: Tolerance exceeded in error handler
 at org.apache.kafka.connect.runtime.errors.RetryWithToleranceOperator.execAndHandleError(RetryWithToleranceOperator.java:178)
…
Caused by: org.apache.kafka.connect.errors.DataException: Converting byte[] to Kafka Connect data failed due to serialization error:
 at org.apache.kafka.connect.json.JsonConverter.toConnectData(JsonConverter.java:334)
…
Caused by: org.apache.kafka.common.errors.SerializationException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('b' (code 98)): was expecting double-quote to start field name
 at [Source: (byte[])"{brokenjson-:"bar 1"}"; line: 1, column: 3]
複製程式碼

要修復管道,需要解決源主題上的訊息問題。除非事先指定,Kafka聯結器是不會簡單地“跳過”無效訊息的。如果是配置錯誤(例如指定了錯誤的序列化轉換器),那最好了,改正之後重新啟動聯結器即可。不過如果確實是該主題的無效訊息,那麼需要找到一種方式,即不要阻止所有其它有效訊息的處理。

靜默忽略無效的訊息

如果只是希望處理一直持續下去:

errors.tolerance = all
複製程式碼
Kafka聯結器深度解讀之錯誤處理和死信佇列

在實際中大概如下:

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_05",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_json",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "file":"/data/file_sink_05.txt",
                "errors.tolerance": "all"
                }
        }'
複製程式碼

啟動聯結器之後(還是原來的源主題,其中既有有效的,也有無效的訊息),就可以持續地執行:

$ curl -s "http://localhost:8083/connectors/file_sink_05/status"| \
    jq -c -M '[.name,.tasks[].state]'
["file_sink_05","RUNNING"]
複製程式碼

這時即使聯結器讀取的源主題上有無效的訊息,也不會有錯誤寫入Kafka聯結器工作節點的輸出,而有效的訊息會按照預期寫入輸出檔案:

$ head data/file_sink_05.txt
{foo=bar 1}
{foo=bar 2}
{foo=bar 3}
…

複製程式碼

是否可以感知資料的丟失?

配置了errors.tolerance = all之後,Kafka聯結器就會忽略掉無效的訊息,並且預設也不會記錄被丟棄的訊息。如果確認配置errors.tolerance = all,那麼就需要仔細考慮是否以及如何知道實際上發生的訊息丟失。在實踐中這意味著基於可用指標的監控/報警,和/或失敗訊息的記錄。

確定是否有訊息被丟棄的最簡單方法,是將源主題上的訊息數與寫入目標端的數量進行對比:

$ kafkacat -b localhost:9092 -t test_topic_json -o beginning -C -e -q -X enable.partition.eof=true | wc -l
     150

$ wc -l data/file_sink_05.txt
     100 data/file_sink_05.txt
複製程式碼

這個做法雖然不是很優雅,但是確實能看出發生了訊息的丟失,並且因為日誌中沒有記錄,所以使用者仍然對此一無所知。

一個更加可靠的辦法是,使用JMX指標來主動監控和報警錯誤訊息率:

Kafka聯結器深度解讀之錯誤處理和死信佇列

這時可以看到發生了錯誤,但是並不知道那些訊息發生了錯誤,不過這是使用者想要的。其實即使之後這些被丟棄的訊息被寫入了/dev/null,實際上也是可以知道的,這也正是死信佇列概念出現的點。

將訊息路由到死信佇列

Kafka聯結器可以配置為將無法處理的訊息(例如上面提到的反序列化錯誤)傳送到一個單獨的Kafka主題,即死信佇列。有效訊息會正常處理,管道也會繼續執行。然後可以從死信佇列中檢查無效訊息,並根據需要忽略或修復並重新處理。

[圖片上傳失敗...(image-9dbb1e-1554814992353)]

進行如下的配置可以啟用死信佇列:

errors.tolerance = all
errors.deadletterqueue.topic.name = 
複製程式碼

如果執行於單節點Kafka叢集,還需要配置errors.deadletterqueue.topic.replication.factor = 1,其預設值為3。

具有此配置的聯結器配置示例大致如下:

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_02",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_json",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "file": "/data/file_sink_02.txt",
                "errors.tolerance": "all",
                "errors.deadletterqueue.topic.name":"dlq_file_sink_02",
                "errors.deadletterqueue.topic.replication.factor": 1
                }
        }'
複製程式碼

使用和之前相同的源主題,然後處理混合有有效和無效的JSON資料,會看到新的聯結器可以穩定執行:

$ curl -s "http://localhost:8083/connectors/file_sink_02/status"| \
    jq -c -M '[.name,.tasks[].state]'
["file_sink_02","RUNNING"]
複製程式碼

源主題中的有效記錄將寫入目標檔案:

$ head data/file_sink_02.txt
{foo=bar 1}
{foo=bar 2}
{foo=bar 3}
[…]
複製程式碼

這樣管道可以繼續正常執行,並且還有了死信佇列主題中的資料,這可以從指標資料中看出:

Kafka聯結器深度解讀之錯誤處理和死信佇列

檢查主題本身也可以看出來:

ksql> LIST TOPICS;

 Kafka Topic            | Registered | Partitions | Partition Replicas | Consumers | ConsumerGroups
---------------------------------------------------------------------------------------------------
 dlq_file_sink_02       | false      | 1          | 1                  | 0         | 0
 test_topic_json        | false      | 1          | 1                  | 1         | 1
---------------------------------------------------------------------------------------------------

ksql> PRINT 'dlq_file_sink_02' FROM BEGINNING;
Format:STRING
1/24/19 5:16:03 PM UTC , NULL , {foo:"bar 1"}
1/24/19 5:16:03 PM UTC , NULL , {foo:"bar 2"}
1/24/19 5:16:03 PM UTC , NULL , {foo:"bar 3"}
…
複製程式碼

從輸出中可以看出,訊息的時間戳為(1/24/19 5:16:03 PM UTC),鍵為(NULL),然後為值。這時可以看到值是無效的JSON格式{foo:"bar 1"}foo也應加上引號),因此JsonConverter在處理時會丟擲異常,因此最終會輸出到死信主題。

但是隻有看到訊息才能知道它是無效的JSON,即便如此,也只能假設訊息被拒絕的原因,要確定Kafka聯結器將訊息視為無效的實際原因,有兩個方法:

  • 死信佇列的訊息頭;
  • Kafka聯結器的工作節點日誌。

下面會分別介紹。

記錄訊息的失敗原因:訊息頭

訊息頭是使用Kafka訊息的鍵、值和時間戳儲存的附加後設資料,是在Kafka 0.11版本中引入的。Kafka聯結器可以將有關訊息拒絕原因的資訊寫入訊息本身的訊息頭中。這個做法比寫入日誌檔案更好,因為它將原因直接與訊息聯絡起來。

配置如下的引數,可以在死信佇列的訊息頭中包含拒絕原因:

errors.deadletterqueue.context.headers.enable = true
複製程式碼

配置示例大致如下:

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_03",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_json",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "file": "/data/file_sink_03.txt",
                "errors.tolerance": "all",
                "errors.deadletterqueue.topic.name":"dlq_file_sink_03",
                "errors.deadletterqueue.topic.replication.factor": 1,
                "errors.deadletterqueue.context.headers.enable":true
                }
        }'
複製程式碼

和之前一致,聯結器可以正常執行(因為配置了errors.tolerance=all)。

$ curl -s "http://localhost:8083/connectors/file_sink_03/status"| \
    jq -c -M '[.name,.tasks[].state]'
["file_sink_03","RUNNING"]
複製程式碼

源主題中的有效訊息會正常寫入目標檔案:

$ head data/file_sink_03.txt
{foo=bar 1}
{foo=bar 2}
{foo=bar 3}
[…]
複製程式碼

可以使用任何消費者工具來檢查死信佇列上的訊息(之前使用了KSQL),不過這裡會使用kafkacat,然後馬上就會看到原因,最簡單的操作大致如下:

kafkacat -b localhost:9092 -t dlq_file_sink_03
% Auto-selecting Consumer mode (use -P or -C to override)
{foo:"bar 1"}
{foo:"bar 2"}
…
複製程式碼

不過kafkacat有更強大的功能,可以看到比訊息本身更多的資訊:

kafkacat -b localhost:9092 -t dlq_file_sink_03 -C -o-1 -c1 \
  -f '\nKey (%K bytes): %k
  Value (%S bytes): %s
  Timestamp: %T
  Partition: %p
  Offset: %o
  Headers: %h\n'
複製程式碼

這個命令將獲取最後一條訊息(-o-1,針對偏移量,使用最後一條訊息),只讀取一條訊息(-c1),並且通過-f引數對其進行格式化,以更易於理解:

Key (-1 bytes):
  Value (13 bytes): {foo:"bar 5"}
  Timestamp: 1548350164096
  Partition: 0
  Offset: 34
  Headers: __connect.errors.topic=test_topic_json,__connect.errors.partition=0,__connect.errors.offset=94,__connect.errors.connector.name=file_sink_03,__connect.errors.task.id=0,__connect.errors.stage=VALU
E_CONVERTER,__connect.errors.class.name=org.apache.kafka.connect.json.JsonConverter,__connect.errors.exception.class.name=org.apache.kafka.connect.errors.DataException,__connect.errors.exception.message=Co
nverting byte[] to Kafka Connect data failed due to serialization error: ,__connect.errors.exception.stacktrace=org.apache.kafka.connect.errors.DataException: Converting byte[] to Kafka Connect data failed
 due to serialization error:
[…]
複製程式碼

也可以只顯示訊息頭,並使用一些簡單的技巧將其拆分,這樣可以更清楚地看到該問題的更多資訊:

$ kafkacat -b localhost:9092 -t dlq_file_sink_03 -C -o-1 -c1 -f '%h'|tr ',' '\n'
__connect.errors.topic=test_topic_json
__connect.errors.partition=0
__connect.errors.offset=94
__connect.errors.connector.name=file_sink_03
__connect.errors.task.id=0
__connect.errors.stage=VALUE_CONVERTER
__connect.errors.class.name=org.apache.kafka.connect.json.JsonConverter
__connect.errors.exception.class.name=org.apache.kafka.connect.errors.DataException
__connect.errors.exception.message=Converting byte[] to Kafka Connect data failed due to serialization error:
複製程式碼

Kafka聯結器處理的每條訊息都來自源主題和該主題中的特定點(偏移量),訊息頭已經準確地說明了這一點。因此可以使用它來回到原始主題並在需要時檢查原始訊息,由於死信佇列已經有一個訊息的副本,這個檢查更像是一個保險的做法。

根據從上面的訊息頭中獲取的詳細資訊,可以再檢查一下源訊息:

__connect.errors.topic=test_topic_json
__connect.errors.offset=94
複製程式碼

將這些值分別插入到kafkacat的代表主題和偏移的-t-o引數中,可以得到:

$ kafkacat -b localhost:9092 -C \
  -t test_topic_json -o94 \
  -f '\nKey (%K bytes): %k
  Value (%S bytes): %s
  Timestamp: %T
  Partition: %p
  Offset: %o
  Topic: %t\n'
複製程式碼
Key (-1 bytes):
  Value (13 bytes): {foo:"bar 5"}
  Timestamp: 1548350164096
  Partition: 0
  Offset: 94
  Topic: test_topic_json
複製程式碼

與死信佇列中的上述訊息相比,可以看到完全相同,甚至包括時間戳,唯一的區別是主題、偏移量和訊息頭。

記錄訊息的失敗原因:日誌

記錄訊息的拒絕原因的第二個選項是將其寫入日誌。根據安裝方式不同,Kafka聯結器會將其寫入標準輸出或日誌檔案。無論哪種方式都會為每個失敗的訊息生成一堆詳細輸出。進行如下配置可啟用此功能:

errors.log.enable = true
複製程式碼

通過配置errors.log.include.messages = true,還可以在輸出中包含有關訊息本身的後設資料。此後設資料中包括一些和上面提到的訊息頭中一樣的專案,包括源訊息的主題和偏移量。注意它不包括訊息鍵或值本身。

這時的聯結器配置如下:

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_04",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_json",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "file": "/data/file_sink_04.txt",
                "errors.tolerance": "all",
                "errors.log.enable":true,
                "errors.log.include.messages":true
                }
        }'
複製程式碼

聯結器是可以成功執行的:

$ curl -s "http://localhost:8083/connectors/file_sink_04/status"| \
    jq -c -M '[.name,.tasks[].state]'
["file_sink_04","RUNNING"]
Valid records from the source topic get written to the target file:
$ head data/file_sink_04.txt
{foo=bar 1}
{foo=bar 2}
{foo=bar 3}
[…]
複製程式碼

這時去看Kafka聯結器的工作節點日誌,會發現每個失敗的訊息都有錯誤記錄:

ERROR Error encountered in task file_sink_04-0. Executing stage 'VALUE_CONVERTER' with class 'org.apache.kafka.connect.json.JsonConverter', where consumed record is {topic='test_topic_json', partition=0, offset=94, timestamp=1548350164096, timestampType=CreateTime}. (org.apache.kafka.connect.runtime.errors.LogReporter)
org.apache.kafka.connect.errors.DataException: Converting byte[] to Kafka Connect data failed due to serialization error:
 at org.apache.kafka.connect.json.JsonConverter.toConnectData(JsonConverter.java:334)
[…]
Caused by: org.apache.kafka.common.errors.SerializationException: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('f' (code 102)): was expecting double-quote to start field name
 at [Source: (byte[])"{foo:"bar 5"}"; line: 1, column: 3]
複製程式碼

可以看到錯誤本身,還有就是和錯誤有關的資訊:

{topic='test_topic_json', partition=0, offset=94, timestamp=1548350164096, timestampType=CreateTime}
複製程式碼

如上所示,可以在kafkacat等工具中使用該主題和偏移量來檢查源主題上的訊息。根據丟擲的異常也可能會看到記錄的源訊息:

Caused by: org.apache.kafka.common.errors.SerializationException:
…
at [Source: (byte[])"{foo:"bar 5"}"; line: 1, column: 3]
複製程式碼

處理死信佇列的訊息

雖然設定了一個死信佇列,但是如何處理那些“死信”呢?因為它只是一個Kafka主題,所以可以像使用任何其它主題一樣使用標準的Kafka工具。上面已經看到了,比如可以使用kafkacat來檢查訊息頭,並且對於訊息的內容及其後設資料的一般檢查kafkacat也可以做。當然根據被拒絕的原因,也可以選擇對訊息進行重播。

一個場景是聯結器正在使用Avro轉換器,但是主題上的卻是JSON格式訊息(因此被寫入死信佇列)。可能由於遺留原因JSON和Avro格式的生產者都在寫入源主題,這個問題得解決,但是目前只需要將管道流中的資料寫入接收器即可。

首先,從初始的接收器讀取源主題開始,使用Avro反序列化並路由到死信佇列:

curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_06__01-avro",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"test_topic_avro",
                "file":"/data/file_sink_06.txt",
                "key.converter": "io.confluent.connect.avro.AvroConverter",
                "key.converter.schema.registry.url": "http://schema-registry:8081",
                "value.converter": "io.confluent.connect.avro.AvroConverter",
                "value.converter.schema.registry.url": "http://schema-registry:8081",
                "errors.tolerance":"all",
                "errors.deadletterqueue.topic.name":"dlq_file_sink_06__01",
                "errors.deadletterqueue.topic.replication.factor":1,
                "errors.deadletterqueue.context.headers.enable":true,
                "errors.retry.delay.max.ms": 60000,
                "errors.retry.timeout": 300000
                }
        }'
複製程式碼

另外再建立第二個接收器,將第一個接收器的死信佇列作為源主題,並嘗試將記錄反序列化為JSON,在這裡要更改的是value.converterkey.converter、源主題名和死信佇列名(如果此聯結器需要將任何訊息路由到死信佇列,要避免遞迴)。

Kafka聯結器深度解讀之錯誤處理和死信佇列
curl -X POST http://localhost:8083/connectors -H "Content-Type: application/json" -d '{
        "name": "file_sink_06__02-json",
        "config": {
                "connector.class": "org.apache.kafka.connect.file.FileStreamSinkConnector",
                "topics":"dlq_file_sink_06__01",
                "file":"/data/file_sink_06.txt",
                "value.converter":"org.apache.kafka.connect.json.JsonConverter",
                "value.converter.schemas.enable": false,
                "key.converter":"org.apache.kafka.connect.json.JsonConverter",
                "key.converter.schemas.enable": false,
                "errors.tolerance":"all",
                "errors.deadletterqueue.topic.name":"dlq_file_sink_06__02",
                "errors.deadletterqueue.topic.replication.factor":1,
                "errors.deadletterqueue.context.headers.enable":true,
                "errors.retry.delay.max.ms": 60000,
                "errors.retry.timeout": 300000
                }
        }'
複製程式碼

現在可以驗證一下。

首先,源主題收到20條Avro訊息,之後可以看到20條訊息被讀取並被原始Avro接收器接收:

Kafka聯結器深度解讀之錯誤處理和死信佇列

然後傳送8條JSON訊息,這時8條訊息被髮送到死信佇列,然後被JSON接收器接收:

Kafka聯結器深度解讀之錯誤處理和死信佇列

現在再傳送5條格式錯誤的JSON訊息,之後可以看到兩者都有失敗的訊息,有2點可以確認:

  1. 從Avro接收器傳送到死信佇列的訊息數與成功傳送的JSON訊息數之間有差異;
  2. 訊息被髮送到JSON接收器的死信佇列。
Kafka聯結器深度解讀之錯誤處理和死信佇列

通過KSQL監控死信佇列

除了使用JMX監控死信佇列之外,還可以利用KSQL的聚合能力編寫一個簡單的流應用來監控訊息寫入佇列的速率:

-- 為每個死信佇列主題註冊流。
CREATE STREAM dlq_file_sink_06__01(MSG VARCHAR)WITH(KAFKA_TOPIC ='dlq_file_sink_06__01',VALUE_FORMAT ='DELIMITED');
CREATE STREAM dlq_file_sink_06__02(MSG VARCHAR)WITH(KAFKA_TOPIC ='dlq_file_sink_06__02',VALUE_FORMAT ='DELIMITED');

-- 從主題的開頭消費資料
SET 'auto.offset.reset' = 'earliest';

-- 使用其它列建立監控流,可用於後續聚合查詢
CREATE STREAM DLQ_MONITOR WITH (VALUE_FORMAT='AVRO') AS \
  SELECT 'dlq_file_sink_06__01' AS SINK_NAME, \
         'Records: ' AS GROUP_COL, \
         MSG \
    FROM dlq_file_sink_06__01;

-- 使用來自第二個死信佇列的訊息注入相同的監控流
INSERT INTO DLQ_MONITOR \
  SELECT 'dlq_file_sink_06__02' AS SINK_NAME, \
         'Records: ' AS GROUP_COL, \
         MSG \
    FROM dlq_file_sink_06__02;

-- 在每個死信佇列每分鐘的時間視窗內,建立訊息的聚合檢視
CREATE TABLE DLQ_MESSAGE_COUNT_PER_MIN AS \
  SELECT TIMESTAMPTOSTRING(WINDOWSTART(),'yyyy-MM-dd HH:mm:ss') AS START_TS, \
         SINK_NAME, \
         GROUP_COL, \
         COUNT(*) AS DLQ_MESSAGE_COUNT \
    FROM DLQ_MONITOR \
          WINDOW TUMBLING (SIZE 1 MINUTE) \
 GROUP BY SINK_NAME, \
          GROUP_COL;
複製程式碼

這個聚合表可以以互動式的方式進行查詢,下面顯示了一分鐘內每個死信佇列中的訊息數量:

ksql> SELECT START_TS, SINK_NAME, DLQ_MESSAGE_COUNT FROM DLQ_MESSAGE_COUNT_PER_MIN;
2019-02-01 02:56:00 | dlq_file_sink_06__01 | 9
2019-02-01 03:10:00 | dlq_file_sink_06__01 | 8
2019-02-01 03:12:00 | dlq_file_sink_06__01 | 5
2019-02-01 02:56:00 | dlq_file_sink_06__02 | 5
2019-02-01 03:12:00 | dlq_file_sink_06__02 | 5
複製程式碼

因為這個表的下面是Kafka主題,所以可以將其路由到期望的任何監控儀表盤,還可以用於驅動告警。假定有幾條錯誤訊息是可以接受的,但是一分鐘內超過5條訊息就是個大問題需要關注:

CREATE TABLE DLQ_BREACH AS \
    SELECT START_TS, SINK_NAME, DLQ_MESSAGE_COUNT \
      FROM DLQ_MESSAGE_COUNT_PER_MIN \
     WHERE DLQ_MESSAGE_COUNT>5;
複製程式碼

現在又有了一個報警服務可以訂閱的DLQ_BREACH主題,當收到任何訊息時,可以觸發適當的操作(例如通知)。

ksql> SELECT START_TS, SINK_NAME, DLQ_MESSAGE_COUNT FROM DLQ_BREACH;
2019-02-01 02:56:00 | dlq_file_sink_06__01 | 9
2019-02-01 03:10:00 | dlq_file_sink_06__01 | 8
複製程式碼

Kafka聯結器哪裡沒有提供錯誤處理?

Kafka聯結器的錯誤處理方式,如下表所示:

聯結器生命週期階段描述是否處理錯誤?
開始首次啟動聯結器時,其將執行必要的初始化,例如連線到資料儲存
拉取(針對源聯結器)從源資料儲存讀取訊息
格式轉換從Kafka主題讀寫資料並對JSON/Avro格式進行序列化/反序列化
單訊息轉換應用任何已配置的單訊息轉換
接收(針對接收聯結器)將訊息寫入目標資料儲存

注意源聯結器沒有死信佇列。

錯誤處理配置流程

關於聯結器錯誤處理的配置,可以按照如下的流程一步步進階:

Kafka聯結器深度解讀之錯誤處理和死信佇列

總結

處理錯誤是任何穩定可靠的資料管道的重要組成部分,根據資料的使用方式,可以有兩個選項。如果管道任何錯誤的訊息都不能接受,表明上游存在嚴重的問題,那麼就應該立即停止處理(這是Kafka聯結器的預設行為)。

另一方面,如果只是想將資料流式傳輸到儲存以進行分析或非關鍵性處理,那麼只要不傳播錯誤,保持管道穩定執行則更為重要。這時就可以定義錯誤的處理方式,推薦的方式是使用死信佇列並密切監視來自Kafka聯結器的可用JMX指標。

最後

大家覺得不錯可以點個贊在關注下,以後還會分享更多文章!


相關文章