Kafka入門教程其一 消息隊列基本概念 及常用Producer Consumer配置詳解學習筆記

1. 綜述

Apache Kafka是基於發佈/訂閱的容錯消息系統,由Scala和Java編寫,是一個分佈式消息隊列,具有高性能、持久化、多副本備份、橫向擴展能力。

與其他消息傳遞系統相比,Kafka具有更好的吞吐量,內置分區,複製和固有的容錯能力,這使得它非常適合大規模消息處理應用程序。

Kafka適合離線和在線消息消費。 Kafka消息保留在磁盤上,並在羣集內複製以防止數據丟失。 Kafka構建在ZooKeeper同步服務之上。 它與Apache Storm和Spark非常好地集成,用於實時流式數據分析。

2. 消息隊列(Message Queue)

Message Queue消息傳送系統提供傳送服務。消息傳送依賴於大量支持組件,這些組件負責處理連接服務、消息的路由和傳送、持久性、安全性以及日誌記錄。消息服務器可以使用一個或多個代理實例。消息隊列分爲兩種:點對點發佈/訂閱(pub-sub)

2.1 點對點

消息生產者生產消息發送到queue中,然後消息消費者從queue中取出並且消費消息。

消息被消費以後,queue中不再有存儲,所以消息消費者不可能消費到已經被消費的消息。Queue支持存在多個消費者,但是對一個消息而言,只會有一個消費者可以消費。

該系統的典型示例是訂單處理系統,其中每個訂單將由一個訂單處理器處理,但多個訂單處理器也可以同時工作。

2.2 發佈/訂閱(pub-sub)

消息生產者(發佈)將消息發佈到topic中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,發佈到topic的消息會被所有訂閱者消費。

現實生活的例子是電視,它發佈不同的頻道,如運動,電影,音樂等,任何人都可以訂閱自己的頻道集。

3. Kafka基礎術語解釋

  • 生產者Producer: 是消息的產生的源頭,負責生成消息併發送到Kafka服務器上。
  • 消費者Consumer: 消息的使用方,負責消費Kafka服務器上的消息。
  • 主題Topic: 由用戶定義並配置在Kafka服務器,用於建立生產者和消息者之間的訂閱關係:生產者發送消息到指定的Topic下,消息者從這個Topic下消費消息。
  • 分區Partition: 一個Topic下面會分爲很多分區,例如:“kafka-test”這個Topic下可以分爲6個分區,分別由兩臺服務器提供,那麼通常可以配置爲讓每臺服務器提供3個分區,假如服務器ID分別爲0、1,則所有的分區爲0-0、0-1、0-2和1-0、1-1、1-2。

    Topic物理上的分組,一個 topic可以分爲多個 partition,每個 partition 是一個有序的隊列。partition中的每條消息都會被分配一個有序的 id(offset)
  • Broker: 即Kafka的服務器,用戶存儲消息,Kafka集羣中的一臺或多臺服務器統稱爲 broker。
  • 消費者分組Group: 用於歸組同類消費者,在Kafka中,多個消費者可以共同消息一個Topic下的消息,每個消費者消費其中的部分消息,這些消費者就組成了一個分組,擁有同一個分組名稱,通常也被稱爲消費者集羣。
  • 偏移量Offset: 消息存儲在Kafka的Broker上,消費者拉取消息數據的過程中需要知道消息在文件中的偏移量,這個偏移量就是所謂的Offset。
  • 領導者Leader: 負責給定分區的所有讀取和寫入的節點。 每個分區都有一個服務器充當Leader。
  • 追隨者Follower: 跟隨領導者指令的節點被稱爲Follower。 如果領導失敗,一個追隨者將自動成爲新的領導者。 跟隨者作爲正常消費者,拉取消息並更新其自己的數據存儲。

3.1 Broker

  • Message在Broker中通Log追加的方式進行持久化存儲。並進行分區(patitions)。

  • 爲了減少磁盤寫入的次數,broker會將消息暫時buffer起來,當消息的個數(或尺寸)達到一定閥值時,再flush到磁盤,這樣減少了磁盤IO調用的次數。

  • Broker沒有副本機制,一旦broker宕機,該broker的消息將都不可用。Message消息是有多份的。

  • Broker不保存訂閱者的狀態,由訂閱者自己保存。

  • 無狀態導致消息的刪除成爲難題(可能刪除的消息正在被訂閱),kafka採用基於時間的SLA(服務水平保證),消息保存一定時間(通常爲7天)後會被刪除。

  • 消息訂閱者可以rewind back到任意位置重新進行消費,當訂閱者故障時,可以選擇最小的offset(id)進行重新讀取消費消息。

3.2 Partitions

  • Kafka基於文件存儲.通過分區,可以將日誌內容分散到多個server上,來避免文件尺寸達到單機磁盤的上限,每個partiton都會被當前server(kafka實例)保存。

  • 可以將一個topic切分多任意多個partitions,來消息保存/消費的效率。

  • 越多的partitions意味着可以容納更多的consumer,有效提升併發消費的能力。

3.3 Message

  • Message消息:是通信的基本單位,每個 producer 可以向一個 topic(主題)發佈一些消息。

  • Kafka中的Message是以topic爲基本單位組織的,不同的topic之間是相互獨立的。每個topic又可以分成幾個不同的partition(每個topic有幾個partition是在創建topic時指定的),每個partition存儲一部分Message。

  • partition中的每條Message包含了以下三個屬性:

    • offset: 消息唯一標識, 對應類型long

    • MessageSize: 對應類型int32

    • data: message的具體內容。

4. Kafka持久化

  1. 一個Topic可以認爲是一類消息,每個topic將被分成多partition(區),每個partition在存儲層面是append log文件。任何發佈到此partition的消息都會被直接追加到log文件的尾部,每條消息在文件中的位置稱爲offset(偏移量),partition是以文件的形式存儲在文件系統中。

  2. Logs文件根據broker中的配置要求,保留一定時間後刪除來釋放磁盤空間。

  1. 爲數據文件建索引:稀疏存儲,每隔一定字節的數據建立一條索引。下圖爲一個partition的索引示意圖:

5. Kafka 作爲消息/存儲系統及流處理

5.1 消息系統

kafka有比傳統的消息系統更強的順序保證。
傳統的消息系統按順序保存數據,如果多個消費者從隊列消費,則服務器按存儲的順序發送消息,但是,儘管服務器按順序發送,消息異步傳遞到消費者,因此消息可能亂序到達消費者。這意味着消息存在並行消費的情況,順序就無法保證。消息系統常常通過僅設1個消費者來解決這個問題,但是這意味着沒用到並行處理。

kafka做的更好。通過並行topic的parition —— kafka提供了順序保證和負載均衡。每個partition僅由同一個消費者組中的一個消費者消費到。並確保消費者是該partition的唯一消費者,並按順序消費數據。每個topic有多個分區,則需要對多個消費者做負載均衡,但請注意,相同的消費者組中不能有比分區更多的消費者,否則多出的消費者一直處於空等待,不會收到消息。

5.2 存儲系統

所有發佈消息到消息隊列和消費分離的系統,實際上都充當了一個存儲系統(發佈的消息先存儲起來)。Kafka比別的系統的優勢是它是一個非常高性能的存儲系統

寫入到kafka的數據將寫到磁盤並複製到集羣中保證容錯性。並允許生產者等待消息應答,直到消息完全寫入。

kafka的磁盤結構 - 無論你服務器上有50KB或50TB,執行是相同的。

client來控制讀取數據的位置。你還可以認爲kafka是一種專用於高性能,低延遲,提交日誌存儲,複製,和傳播特殊用途的分佈式文件系統

以下是詳細說明:可以認爲topic下有partition,partition下有segment,segment是實際的一個個文件,topic和partition都是抽象概念。

在目錄/${topicName}-{$partitionid}/下,存儲着實際的log文件(即segment),還有對應的索引文件。

每個segment文件大小相等,文件名以這個segment中最小的offset命名,文件擴展名是.log;segment對應的索引的文件名字一樣,擴展名是.index。有兩個index文件,一個是offset index用於按offset去查message,一個是time index用於按照時間去查,其實這裏可以優化合到一起,下面只說offset index。總體的組織是這樣的:

爲了減少索引文件的大小,降低空間使用,方便直接加載進內存中,這裏的索引使用稀疏矩陣,不會每一個message都記錄下具體位置,而是每隔一定的字節數,再建立一條索引。 索引包含兩部分,分別是baseOffset,還有position

  • baseOffset:意思是這條索引對應segment文件中的第幾條message。這樣做方便使用數值壓縮算法來節省空間。例如kafka使用的是varint。

  • position:在segment中的絕對位置。

查找offset對應的記錄時,會先用二分法,找出對應的offset在哪個segment中,然後使用索引,在定位出offset在segment中的大概位置,再遍歷查找message。

5.3 流處理

在kafka中,流處理持續獲取輸入topic的數據,進行處理加工,然後寫入輸出topic

可以直接使用producer和consumer API進行簡單的處理。對於複雜的轉換,Kafka提供了更強大的Streams API。可構建聚合計算或連接流到一起的複雜應用程序。

助於解決此類應用面臨的硬性問題:處理無序的數據,代碼更改的再處理,執行狀態計算等。

前面的博客Spark Structured Streaming + Kafka使用筆記有詳細介紹Spark+Kafka的使用。

6. 常用配置項

6.1 Kafka實例配置

6.1.1 broker配置
配置項 作用
broker.id broker的唯一標識
auto.create.topics.auto 設置成true,就是遇到沒有的topic自動創建topic。
log.dirs log的目錄數,目錄裏面放partition,當生成新的partition時,會挑目錄裏partition數最少的目錄放。
6.1.2 topic配置
配置項 作用
num.partitions 新建一個topic,會有幾個partition。
log.retention.ms 對應的還有minutes,hours的單位。日誌保留時間,因爲刪除是文件維度而不是消息維度,看的是日誌文件的mtime。
log.retention.bytes partion最大的容量,超過就清理老的。注意這個是partion維度,就是說如果你的topic有8個partition,配置1G,那麼平均分配下,topic理論最大值8G。
log.segment.bytes 一個segment的大小。超過了就滾動。
log.segment.ms 一個segment的打開時間,超過了就滾動。
message.max.bytes message最大多大

6.2 Producer端配置

優先級高
  • bootstrap.servers

    一組host和port用於初始化連接. 不管這裏配置了多少臺server, 都只是用作發現整個集羣全部server信息. 這個配置不需要包含集羣所有的機器信息. 但是最好多於一個, 以防服務器掛掉.

  • key.serializer

    用來序列化key的Serializer接口的實現類.

  • value.serializer

    用來序列化value的Serializer接口的實現類

  • acks

    producer希望leader返回的用於確認請求完成的確認數量. 可選值 all, -1, 0 1. 默認值爲1

    • acks=0 不需要等待服務器的確認. 這是retries設置無效. 響應裏來自服務端的offset總是-1. producer只管發不管發送成功與否。延遲低,容易丟失數據。
    • acks=1 表示leader寫入成功(但是並沒有刷新到磁盤)後即向producer響應。延遲中等,一旦leader副本掛了,就會丟失數據。
    • acks=all等待數據完成副本的複製, 等同於-1. 假如需要保證消息不丟失, 需要使用該設置. 同時需要設置unclean.leader.election.enable爲true, 保證當ISR列表爲空時, 選擇其他存活的副本作爲新的leader.
  • buffer.memory

    producer可以使用的最大內存來緩存等待發送到server端的消息. 如果消息速度大於producer交付到server端的阻塞時間max.block.ms, 將會拋出異常. 默認值33554432 byte (32m). 這個設置不是一個嚴格的邊界, 因爲producer除了用來緩存消息, 還要用來進行壓縮.

  • compression.type

    producer壓縮數據的類型, 默認爲none, 就是不壓縮. 可選none, gzip, snappylz4. 壓縮整個batch的數據, 因此batch的效果對壓縮率也有影響. 更多的批處理意味着更好的壓縮

  • retries

    設置大於零的值將導致客戶端重新發送其發送失敗併發生潛在的瞬時錯誤的記錄. 相當於client在發送失敗的時候會重新發行. 如果設置了retries而沒有將max.in.flight.request.per.connection設置爲1, 在兩個batch發送到同一個partition時有可能打亂消息的發送順序(第一個發送失敗, 而第二個發送成功)

優先級中
  • batch.size

    producer會嘗試批量發送屬於同一個partition的消息以減少請求的數量. 這樣可以提升客戶端和服務端的性能. 默認大小是16348 byte (16k).

    發送到broker的請求可以包含多個batch, 每個batch的數據屬於同一個partition.

    太小的batch會降低吞吐. 太大會浪費內存.

  • client.id

    發送請求時傳遞給服務端的id字符. 用來追溯請求源, 除了使用ip/port. 服務端的請求日誌中會包含一個合理的應用名. 默認爲空

  • linger.ms

    在正常負載的情況下, 要想減少請求的數量. 加上一個認爲的延遲: 不是立即發送消息, 而是延遲等待更多的消息一起批量發送. 類似TCP中的Nagle算法. 當獲得了batch.size的同一partition的消息會立即發送, 不管linger.ms的設置. 假如要發送的消息比較少, 會等待指定的時間以獲取更多的消息.

    默認設置爲0 ms(沒有延遲).

  • max.block.ms

    控制KafkaProducer.send()KafkaProducer.partitionsFor()的阻塞時間. 這些方法會因爲buffer滿了或者metadata不可用而阻塞. 用戶設置在serializers或者partitioner中的阻塞不會計算在內.

  • max.request.size

    請求的最大大小(以字節爲單位)。 此設置將限制生產者在單個請求中發送的記錄批次數,以避免發送巨大的請求。 這也是最大記錄批量大小的上限。 請注意,服務器擁有自己的記錄批量大小,可能與此不同。

  • partitioner.class

    Partitioner接口的實現類, 默認是org.apache.kafka.clients.producer.internals.DefaultPartitioner. 需要處理數據傾斜等原因調整分區邏輯的時候使用.

  • request.timeout.ms

    配置控制客戶端等待請求響應的最長時間。 如果在超時之前未收到響應,客戶端將在必要時重新發送請求,如果重試耗盡,則該請求將失敗。 這應該大於replica.lag.time.max.ms(broker配置),以減少由於不必要的生產者重試引起的消息重複的可能性。

優先級低
  • enable.idempotence

    設置爲’true’, 將開啓exactly-once模式. 設置爲’false’(默認值), producer會因爲borker失敗等原因重試發送, 可能會導致消息重複.

    設置爲’true’時需要結合max.in.flight.requests.per.connection設爲’1’和retires不能爲’0’, 同時acks需要設置爲’all’或者’’-1’.

  • interceptor.classes

    一組ProducerInterceptor接口的實現類, 默認爲null. 可以通過該接口的實現類去攔截(可能需要修改)producer要發送的消息在發送到服務端之前.

  • max.in.flight.requests.per.connection

    沒有被確認unacknowledge的batch數, 如果設置大於1在retries設置了的情況下會出現消息發送順序錯誤.

  • retry.backoff.ms

    失敗請求重試的間隔時間. 默認是100毫秒

  • transaction.timeout.ms

    事務協調器等待producer更新事務狀態的最大毫秒數, 超過的話事務協調器會終止進行中的事務. 如果設置的時間大於broker的max.transaction.timeout.ms會收到InvalidTransactionTimeout錯誤.

  • transactional.id

    用於事務傳遞的TransactionalId。 這使得可以跨越多個生產者會話的可靠性語義,因爲它允許客戶端保證在開始任何新事務之前使用相同的TransactionalId的事務已經完成。 如果沒有提供TransactionalId,則生產者被限制爲冪等傳遞。 請注意,如果配置了TransactionalId,則必須啓用enable.idempotence。 默認值爲空,這意味着無法使用事務。

6.3 Consumer端配置

  • consumer.poll(1000)

    新版本的Consumer的Poll方法使用了類似於Select I/O機制,因此所有相關事件(包括reblance,消息獲取等)都發生在一個事件循環之中。
    1000是一個超時時間,一旦拿到足夠多的數據(參數設置),consumer.poll(1000)會立即返回 ConsumerRecords<String, String> records
    如果沒有拿到足夠多的數據,會阻塞1000ms,但不會超過1000ms就會返回。

  • session. timeout. ms <= coordinator檢測失敗的時間

    默認值是10s
    該參數是 Consumer Group 主動檢測 (組內成員comsummer)崩潰的時間間隔。若設置10min,那麼Consumer Group的管理者(group coordinator)可能需要10分鐘才能感受到。太漫長了是吧。

  • max. poll. interval. ms <= 處理邏輯最大時間

    這個參數是0.10.1.0版本後新增的,可能很多地方看不到喔。這個參數需要根據實際業務處理時間進行設置,一旦Consumer處理不過來,就會被踢出Consumer Group

    注意:如果業務平均處理邏輯爲1分鐘,那麼max. poll. interval. ms需要設置稍微大於1分鐘即可,但是session. timeout. ms可以設置小一點(如10s),用於快速檢測Consumer崩潰。

  • auto.offset.reset

    該屬性指定了消費者在讀取一個沒有偏移量後者偏移量無效(消費者長時間失效當前的偏移量已經過時並且被刪除了)的分區的情況下,應該作何處理,默認值是latest,也就是從最新記錄讀取數據(消費者啓動之後生成的記錄),另一個值是earliest,意思是在偏移量無效的情況下,消費者從起始位置開始讀取數據。

  • enable.auto.commit

    對於精確到一次的語義,最好手動提交位移

  • fetch.max.bytes

    單次獲取數據的最大消息數。

  • max.poll.records <= 吞吐量

    單次poll調用返回的最大消息數,如果處理邏輯很輕量,可以適當提高該值。
    一次從kafka中poll出來的數據條數,max.poll.records條數據需要在在session.timeout.ms這個時間內處理完
    默認值爲500

  • heartbeat. interval. ms <= 居然拖家帶口

    heartbeat心跳主要用於溝通交流,及時返回請求響應。這個時間間隔真是越快越好。因爲一旦出現reblance,那麼就會將新的分配方案或者通知重新加入group的命令放進心跳響應中。

  • connection. max. idle. ms <= socket連接

    kafka會定期的關閉空閒Socket連接。默認是9分鐘。如果不在乎這些資源開銷,推薦把這些參數值爲-1,即不關閉這些空閒連接。

  • request. timeout. ms

    這個配置控制一次請求響應的最長等待時間。如果在超時時間內未得到響應,kafka要麼重發這條消息,要麼超過重試次數的情況下直接置爲失敗。
    消息發送的最長等待時間.需大於session.timeout.ms這個時間

  • fetch.min.bytes

    server發送到消費端的最小數據,若是不滿足這個數值則會等待直到滿足指定大小。默認爲1表示立即接收。

  • fetch.wait.max.ms

    若是不滿足fetch.min.bytes時,等待消費端請求的最長等待時間

Ref

  1. https://www.jianshu.com/p/d3e963ff8b70
  2. https://www.w3cschool.cn/apache_kafka/apache_kafka_introduction.html
  3. https://blog.csdn.net/dapeng1995/article/details/81536862
  4. http://orchome.com/5
  5. https://atbug.com/kafka-producer-config/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章