kafka學習筆記總結

kafka學習筆記總結

參考:
http://orchome.com/kafka/index
https://blog.csdn.net/qq_24084925/article/details/78842844

角色說明

Message
    通信的基本單位,每個 producer 可以向一個 topic(主題)發佈一些消息。
Producer
    消息生產者,是消息的產生的源頭,負責生成消息併發送到Kafka 服務器上。
Consumer
    消息消費者,是消息的使用方,負責消費Kafka服務器上的消息。
Consumer group:
  high-level consumer API 中,每個 consumer 都屬於一個 consumer group,每條消息只能被 consumer group 中的一個 Consumer 消費,但可以被多個 consumer group 消費。
Topic
    主題,由用戶定義並配置在Kafka服務器,用於建立生產者和消息者之間的訂閱關係:生產者發送消息到指定的Topic下,消息者從這個Topic下消費消息。
Broker
    即Kafka的服務器,用戶存儲消息,Kafa集羣中的一臺或多臺服務器統稱爲 broker
Group
    消費者分組,用於歸組同類消費者,在Kafka中,多個消費者可以共同消費一個Topic下的消息,每個消費者消費其中的部分消息,這些消費者就組成了一個分組,擁有同一個分組名稱,通常也被稱爲消費者集羣。
Offset
    消息存儲在Kafka的Broker上,消費者拉取消息數據的過程中需要知道消息在文件中的偏移量,這個偏移量就是所謂的Offset。
partition:
    kafka 分配的單位是 partition。
  partition 是物理上的概念,每個 topic 包含一個或多個 partition。
    一個 topic可以分爲多個 partition,每個 partition 是一個有序的隊列。
    partition中的每條消息都會被分配一個有序的 id(offset)
replica:
  partition 的副本,保障 partition 的高可用。
leader:
  replica 中的一個角色, producer 和 consumer 只跟 leader 交互。
    Leader 是負責給定分區的所有讀取和寫入的節點。 
    每個分區都有一個服務器充當Leader
follower:
  replica 中的一個角色,從 leader 中複製數據。
controller:
  kafka 集羣中的其中一個服務器,用來進行 leader election 以及 各種 failover。
zookeeper:
  kafka 通過 zookeeper 來存儲集羣的 meta 信息。

概念

我們認爲,一個流處理平臺具有三個關鍵能力:

發佈和訂閱消息(流),在這方面,它類似於一個消息隊列或企業消息系統;
以容錯的方式存儲消息(流);
在消息流發生時處理它們;

kafka應用於2大類應用:

構建實時的流數據管道,可靠地獲取系統和應用程序之間的數據。
構建實時流的應用程序,對數據流進行轉換或反應

一些基本概念(The high-level consumer API):

  1. kafka作爲一個集羣運行在一個或多個服務器上

  2. kafka集羣存儲的消息是以topic爲類別記錄的,不同的topic之間是相互獨立的

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

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

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

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

  7. 每個topic可以分成幾個不同的partition(每個topic有幾個partition是在創建topic時指定的),每個partition存儲一部分Message。

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

    offset 即:消息唯一標識:對應類型:long
    MessageSize 對應類型:int32
    data 是message的具體內容。

  9. kafka 的分配單位是 patition,每個 consumer 都屬於一個 group,一個 partition 只能被同一個 group 內的一個 consumer 所消費;
    保障了一個消息只能被 group 內的一個 consuemr 所消費,但是多個 group 可以同時消費這個 partition;

  10. 一個consumer可以消費多個partitions中的消息(消費者數據小於Partions的數量時),consumer 採用 pull 模式從 broker 中讀取數據;

  11. kafka的設計原理決定,對於一個topic,同一個group中不能有多於partitions個數的consumer同時消費,否則將意味着某些consumer將無法得到消息;

  12. 一個Topic可以認爲是一類消息,每個topic將被分成多partition(區),每個partition在存儲層面是append log文件;

  13. partition是以文件的形式存儲在文件系統中的,任何發佈到此partition的消息都會被直接追加到log文件的尾部 - 屬於順序寫磁盤(順序寫磁盤效率比隨機寫內存要高,保障 kafka 吞吐率)

  14. partition的數據文件索引基於稀疏存儲,每隔一定字節的數據建立一條索引;

  15. 分區中的消息都被分了一個序列號,稱之爲偏移量(offset),在每個分區中此偏移量都是唯一的(Kafka 保證一個 Partition 內的消息的有序性);

通訊過程:

客戶端打開與服務器端的Socket
往Socket寫入一個int32的數字(數字表示這次發送的Request有多少字節)
服務器端先讀出一個int32的整數從而獲取這次Request的大小
然後讀取對應字節數的數據從而得到Request的具體內容
服務器端處理了請求後,也用同樣的方式來發送響應。

kafka有四個核心API:

應用程序使用 Producer API 發佈消息到1個或多個topic(主題)。
應用程序使用 Consumer API 來訂閱一個或多個topic,並處理產生的消息。
應用程序使用 Streams API 充當一個流處理器,從1個或多個topic消費輸入流,並生產一個輸出流到1個或多個輸出topic,有效地將輸入流轉換到輸出流。
Connector API允許構建或運行可重複使用的生產者或消費者,將topic連接到現有的應用程序或數據系統。例如,一個關係數據庫的連接器可捕獲每一個變化。

工作流程

消費模式:

The high-level consumer API

high-level consumer API 提供了 consumer group 的語義;
一個消息只能被 group 內的一個 consumer 所消費,且 consumer 消費消息時不關注 offset,最後一個 offset 由 zookeeper 保存。

The SimpleConsumer API

如果你想要對 patition 有更多的控制權,那就應該使用 SimpleConsumer API,比如:
    1. 多次讀取一個消息
    2. 只消費一個 patition 中的部分消息
    3. 使用事務來保證一個消息僅被消費一次
但是使用此 API 時,partition、offset、broker、leader 等對你不再透明,需要自己去管理。你需要做大量的額外工作:
    1. 必須在應用程序中跟蹤 offset,從而確定下一條應該消費哪條消息
    2. 應用程序需要通過程序獲知每個 Partition 的 leader 是誰
    3. 需要處理 leader 的變更
使用 SimpleConsumer API 的一般流程如下:
    1. 查找到一個“活着”的 broker,並且找出每個 partition 的 leader
    2. 找出每個 partition 的 follower
    3. 定義好請求,該請求應該能描述應用程序需要哪些數據
    4. fetch 數據
    5. 識別 leader 的變化,並對之作出必要的響應

發佈-訂閱消息的工作流程(The high-level consumer API)

    生產者定期向主題發送消息。
    Kafka代理存儲爲該特定主題配置的分區中的所有消息。 它確保消息在分區之間平等共享。 如果生產者發送兩個消息並且有兩個分區,Kafka將在第一分區中存儲一個消息,在第二分區中存儲第二個消息。
    消費者訂閱特定主題。
    一旦消費者訂閱主題,Kafka將向消費者提供主題的當前偏移,並且還將偏移保存在Zookeeper系綜中。
    消費者將定期請求Kafka新消息。
    一旦Kafka收到來自生產者的消息,它將這些消息轉發給消費者。
    消費者將收到消息並進行處理。
    一旦消息被處理,消費者將向Kafka代理髮送確認。
    一旦Kafka收到確認,它將偏移更改爲新值,並在Zookeeper中更新它。 由於偏移在Zookeeper中維護,消費者可以正確地讀取下一封郵件。
    以上流程將重複,直到消費者停止請求。
    消費者可以隨時回退/跳到所需的主題偏移量,並閱讀所有後續消息。

訂閱具有相同 Group ID 的主題的消費者被認爲是單個組,並且消息在它們之間共享。 讓我們檢查這個系統的實際工作流程:

隊列消息/用戶組的工作流(The high-level consumer API)

    生產者以固定間隔向某個主題發送消息。
    Kafka存儲在爲該特定主題配置的分區中的所有消息,類似於前面的方案。
    單個消費者訂閱特定主題,假設 Topic ID爲Topic-01,Group ID 爲 Group-1 。
    Kafka以與發佈 - 訂閱消息相同的方式與消費者交互,直到新消費者以相同的組ID 訂閱相同主題 Topic-01  1 。
    一旦新消費者到達,Kafka將其操作切換到共享模式,並在兩個消費者之間共享數據。 此共享將繼續,直到用戶數達到爲該特定主題配置的分區數。
    一旦消費者的數量超過分區的數量,新消費者將不會接收任何進一步的消息,直到現有消費者取消訂閱。 出現這種情況是因爲Kafka中的每個消費者將被分配至少一個分區(一個分區同時最多分配給一個消費者),並且一旦所有分區被分配給現有消費者,新消費者將必須等待。
    此功能也稱爲消費者組。

ZooKeeper的作用

    Apache Kafka的一個關鍵依賴是Apache Zookeeper,它是一個分佈式配置和同步服務。 Zookeeper是Kafka代理和消費者之間的協調接口。 Kafka服務器通過Zookeeper集羣共享信息。 Kafka在Zookeeper中存儲基本元數據,例如關於主題,代理,消費者偏移(隊列讀取器)等的信息。
    由於所有關鍵信息存儲在Zookeeper中,並且它通常在其整體上覆制此數據,因此Kafka代理/ Zookeeper的故障不會影響Kafka集羣的狀態。 Kafka將恢復狀態,一旦Zookeeper重新啓動。 這爲Kafka帶來了零停機時間。 Kafka代理之間的領導者選舉也通過使用Zookeeper在領導者失敗的情況下完成。

這裏寫圖片描述

Topic創建、刪除流程

//創建
1. controller 在 ZooKeeper 的 /brokers/topics 節點上註冊 watcher,當 topic 被創建,則 controller 會通過 watch 得到該 topic 的 partition/replica 分配。
2. controller從 /brokers/ids 讀取當前所有可用的 broker 列表,對於 set_p 中的每一個 partition:
2.1 從分配給該 partition 的所有 replica(稱爲AR)中任選一個可用的 broker 作爲新的 leader,並將AR設置爲新的 ISR
2.2 將新的 leader 和 ISR 寫入 /brokers/topics/[topic]/partitions/[partition]/state
3. controller 通過 RPC 向相關的 broker 發送 LeaderAndISRRequest

//刪除
1. controller 在 zooKeeper 的 /brokers/topics 節點上註冊 watcher,當 topic 被刪除,則 controller 會通過 watch 得到該 topic 的 partition/replica 分配。
2. 若 delete.topic.enable=false,結束;否則 controller 註冊在 /admin/delete_topics 上的 watch 被 fire,controller 通過回調向對應的 broker 發送 StopReplicaRequest。

寫入流程

  1. producer 先從 zookeeper 的 “/brokers/…/state” 節點找到該 partition 的 leader
  2. producer 將消息發送給該 leader
  3. leader 將消息寫入本地 log
  4. followers 從 leader pull 消息,寫入本地 log 後 leader 發送 ACK
  5. leader 收到所有 ISR 中的 replica 的 ACK 後,增加 HW(high watermark,最後 commit 的 offset) 並向 producer 發送 ACK

數據傳輸的事務定義

  1. At most once 消息可能會丟,但絕不會重複傳輸
  2. At least one 消息絕不會丟,但可能會重複傳輸
  3. Exactly once 每條消息肯定會被傳輸一次且僅傳輸一次

當 producer 向 broker 發送消息時,一旦這條消息被 commit,由於 replication 的存在,它就不會丟;
但是如果 producer 發送數據給 broker 後,遇到網絡問題而造成通信中斷,那 Producer 就無法判斷該條消息是否已經 commit;
雖然 Kafka 無法確定網絡故障期間發生了什麼,但是 producer 可以生成一種類似於主鍵的東西,發生故障時冪等性的重試多次,這樣就做到了 Exactly once,但目前還並未實現;
所以目前默認情況下一條消息從 producer 到 broker 是確保了 At least once,可通過設置 producer 異步發送實現At most once。

kafka-php使用

參考:
https://github.com/weiboad/kafka-php/blob/master/README_CH.md
https://github.com/weiboad/kafka-php/blob/master/docs/ch/Configure.md

使用 Composer 安裝

    添加 composer 依賴 nmred/kafka-php 到項目的 composer.json 文件中即可,如:

    {
        "require": {
            "nmred/kafka-php": "0.2.*"
        }
    }

Produce:

異步回調方式調用

<?php
    require '../vendor/autoload.php';
    date_default_timezone_set('PRC');
    use Monolog\Logger;
    use Monolog\Handler\StdoutHandler;
    // Create the logger
    $logger = new Logger('my_logger');
    // Now add some handlers
    $logger->pushHandler(new StdoutHandler());

    $config = \Kafka\ProducerConfig::getInstance();
    $config->setMetadataRefreshIntervalMs(10000);
    $config->setMetadataBrokerList('10.13.4.159:9192');
    $config->setBrokerVersion('0.9.0.1');
    $config->setRequiredAck(1);
    $config->setIsAsyn(false);
    $config->setProduceInterval(500);
    $producer = new \Kafka\Producer(function() {
        return array(
            array(
                'topic' => 'test',
                'value' => 'test....message.',
                'key' => 'testkey',
            ),
        );
    });
    $producer->setLogger($logger);
    $producer->success(function($result) {
        var_dump($result);
    });
    $producer->error(function($errorCode) {
        var_dump($errorCode);
    });
    $producer->send(true);

同步方式調用

<?php
    require '../vendor/autoload.php';
    date_default_timezone_set('PRC');
    use Monolog\Logger;
    use Monolog\Handler\StdoutHandler;
    // Create the logger
    $logger = new Logger('my_logger');
    // Now add some handlers
    $logger->pushHandler(new StdoutHandler());

    $config = \Kafka\ProducerConfig::getInstance();
    $config->setMetadataRefreshIntervalMs(10000);
    $config->setMetadataBrokerList('127.0.0.1:9192');
    $config->setBrokerVersion('0.9.0.1');
    $config->setRequiredAck(1);
    $config->setIsAsyn(false);
    $config->setProduceInterval(500);
    $producer = new \Kafka\Producer();
    $producer->setLogger($logger);

    for($i = 0; $i < 100; $i++) {
            $result = $producer->send(array(
                    array(
                            'topic' => 'test1',
                            'value' => 'test1....message.',
                            'key' => '',
                    ),
            ));
            var_dump($result);
    }

Consumer

<?php
    require '../vendor/autoload.php';
    date_default_timezone_set('PRC');
    use Monolog\Logger;
    use Monolog\Handler\StdoutHandler;
    // Create the logger
    $logger = new Logger('my_logger');
    // Now add some handlers
    $logger->pushHandler(new StdoutHandler());

    $config = \Kafka\ConsumerConfig::getInstance();
    $config->setMetadataRefreshIntervalMs(10000);
    $config->setMetadataBrokerList('10.13.4.159:9192');
    $config->setGroupId('test');
    $config->setBrokerVersion('0.9.0.1');
    $config->setTopics(array('test'));
    //$config->setOffsetReset('earliest');
    $consumer = new \Kafka\Consumer();
    $consumer->setLogger($logger);
    $consumer->start(function($topic, $part, $message) {
        var_dump($message);
    });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章