Kafka 處理器客戶端介紹

OneAPM官方技術部落格發表於2016-06-01

【編者按】本文作者為 Bill Bejeck,主要介紹如何有效利用新的 Apache Kafka 客戶端來滿足資料處理需求。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現,以下為正文。

如果你使用的系統需要傳輸大量資料,就算你沒用過 Kafka,很有可能已經聽說過它了。從較高層面來說,Kafka 是一個對錯誤零容忍、分散式的釋出訂閱資訊系統,設計目的是提供高速服務及處理成千上萬資訊的能力。Kafka 提供多種應用,其中一種是實時處理。實時處理通常包括讀取主題(源)的資料進行分析或轉換工作,然後將結果寫入另一個主題(sink)。目前要完成這些工作,你有以下兩種備選:

  1. 通過 KafkaConsumer 使用自定義程式碼來讀取資料,然後通過 KafkaProducer 寫出資料。
  2. 使用發展成熟的流處理框架,例如 Spark SteamingFlink 或者 Storm

雖然兩種方法都很好,在某些情況下,如果能有一個處於兩種之間的方法就更好了。為此,《Kafka 改進方案》流程提出了一個處理器介面。處理器介面的目的是引入一個客戶端,以便處理 Kafka 消耗的資料,並將結果寫入 Kafka。該處理器客戶端包括兩個組成部分:

  1. 一個“低層級”的處理器,能夠提供資料處理、組合處理和本地狀態儲存的介面
  2. 一個“高層級”的流 DSL,能夠滿足大部分處理執行需求。

接下來將會有一系列文章介紹新的 Kafka 處理器客戶端,本文主要介紹“低層級”的處理器功能。在後續文章中,將會介紹“高層級”的 DSL 和涉及到其它技術的高階用例。如想了解處理器客戶端的動機和目標的完整描述,請閱讀《方案》原文。免責宣告:本人與 Confluent 並無任何關係,僅僅是 Kafka 的一名熱心使用者。

處理器介面的潛在用例

在筆者看來,處理器介面是個有用工具的原因有以下幾點:

  1. 在處理奇異值時需要發出通知或警報。換句話說,業務需求就是:你不需要建立模型或在其它被處理的資料語境中檢查這個值。舉個例子,當有人使用虛假信用卡時,你希望能立即收到通知。
  2. 在進行分析時篩選資料。理想狀態下,篩選出中到高比例的資料應該重新分割槽,來避免資料傾斜問題。分割槽操作成本很高,因此通過篩選哪些資料要傳送到你的分析群集,就可以省去篩選和重新分割槽步驟。
  3. 你只想對源資料的某一部分進行分析,同時把所有資料傳輸到另一個儲存空間。

第一個處理器範例

在第一個處理器範例中,筆者要轉化虛構的客戶購買資料,並進行以下操作:

  1. 掩藏信用卡號的處理器。
  2. 用於收集客戶名字和消費金額的處理器,這些資訊將會用於一個獎勵專案。
  3. 用於收集郵編和購買商品的處理器,這些資訊可以幫助判斷消費者的購物模式。

以下是處理器物件的簡要介紹。三個處理器都繼承了 AbstractProcessor 類,該類提供了punctuateclose 方法的空操作重寫。在本範例中,只需要實現 process 方法,該行為就會執行到每條資訊。任務完成後,將會呼叫 context().forward方法,它會將修改後的或者新的鍵值對轉發給下游的消費者。(context() 方法會檢索 init 方法在父類中預置的 context 例項變數)。然後, context().commit 方法被呼叫,提交包括資訊偏移在內的流當前狀態。

打造處理器圖形

現在需要定義有向無環圖(DAG)來決定資訊的流向。這是關係到處理器介面是否會“出現偏差”的地方。要打造處理器節點圖,需要用到拓撲構造器(ToplogyBuilder)。雖然筆者的資訊屬於 JSON,但還是需要定義 序列化反序列化例項,因為處理器按照型別來處理。下面是來自 PurchaseProcessorDriver的一部分程式碼,它們構成了圖形拓撲、序列化程式和反序列化程式。

//Serializers for types used in the processors
JsonDeserializer<Purchase> purchaseJsonDeserializer = new JsonDeserializer<>(Purchase.class);
JsonSerializer<Purchase> purchaseJsonSerializer = new JsonSerializer<>();
JsonSerializer<RewardAccumulator> rewardAccumulatorJsonSerializer = new JsonSerializer<>();
JsonSerializer<PurchasePattern> purchasePatternJsonSerializer = new JsonSerializer<>();

StringDeserializer stringDeserializer = new StringDeserializer();
StringSerializer stringSerializer = new StringSerializer();

 TopologyBuilder topologyBuilder = new TopologyBuilder();
 topologyBuilder.addSource("SOURCE", stringDeserializer, purchaseJsonDeserializer, "src-topic")
    .addProcessor("PROCESS", CreditCardAnonymizer::new, "SOURCE")
    .addProcessor("PROCESS2", PurchasePatterns::new, "PROCESS")
    .addProcessor("PROCESS3", CustomerRewards::new, "PROCESS")
    .addSink("SINK", "patterns", stringSerializer, purchasePatternJsonSerializer, "PROCESS2")
    .addSink("SINK2", "rewards",stringSerializer, rewardAccumulatorJsonSerializer, "PROCESS3")
    .addSink("SINK3", "purchases", stringSerializer, purchaseJsonSerializer, "PROCESS");
//Use the topologyBuilder and streamingConfig to start the kafka streams process
KafkaStreams streaming = new KafkaStreams(topologyBuilder, streamingConfig);
streaming.start();

There’s several steps here, so let’s do a quick walkthrough

上面的程式碼涉及到幾個步驟,以下是其簡介:

  1. 在第11行有個源節點叫做“SOURCE”,一個用於鍵的 StringDeserializer 以及生成的 JsonSerializer 來處理 Purchase 物件,和供給原始碼的1到N個主題。本範例中使用的是1個主題“src-topic”的輸入資訊。
  2. 接下來開始新增處理器節點。addProcessor 方法以一個 Strings 命名,一個 ProcessorSupplier,以及1到N個父節點。在本範例中,第一個處理器是“SOURCE”節點的孩子,同時又是後兩個處理器的父親。在這裡需要注意 ProcessorSupplier 的句法。該程式碼在利用方法處理(method handles),後者可以在 Java8中用作供應例項的 lambda 表示式。程式碼繼續用同樣的方式定義接下來的兩個處理器。
  3. 最後新增 sink(輸出主題)來完成資訊通道。addSink 方法用到一個 String 名字、主題名字、鍵值序列化程式、值序列化程式和1到 N 個父節點。在3個 addSink 方法中,可以看到之前在程式碼中建立的 JSONDeserializer 物件。

下面是拓撲構造器(TopologyBuilder)的最終結果圖示:

Kafka 處理器客戶端介紹

狀態處理器

處理器介面並不僅限於處理當前收到的值,還能維護集合、總和過程中使用的狀態,或者連線將要收到的資訊。為了充分利用狀態處理功能,在建立處理拓撲時,使用 TopologyBuilder.addStateStore 方法建立一個 KeyValueStore。可以建立兩種儲存區:(1)記憶體式的;(2)利用堆外儲存的 RocksDB 儲存。選擇哪個取決於值的有效時長。對於數量較多的動態值,RocksDB 比較適合,對於時效較短的詞條,記憶體式更適合。在指定 String、Integer 或較長的鍵值和值時,Stores 類別提供序列化反序列化例項。但是如果使用自定義型別的鍵值或值時,需要提供自定義的序列化程式和反序列化程式。

狀態處理器範例

在本範例中,將會看到 process 方法和另外兩個重寫方法:initpunctuateprocess 方法抽取股票代號,更新或建立交易資訊,然後把彙總結果放入儲存區。

init 方法中的操作有:

  1. 設定 ProcessorContext 引用。
  2. ProcessorContext.schedule 方法,以便控制 punctuate 方法的執行頻率。在本範例中頻率為10秒1次。
  3. 給構建 TopologyBuilder(下文將會談到)時建立的狀態儲存區設定一個引用。

punctuate 方法會迭代儲存區內的所有值,並且一旦它們在過去11秒內更新,StockTransactionSummary 物件就會被髮送給消費者。

利用狀態儲存區打造一個拓撲構造器

跟前面的例子一樣,看處理器程式碼只完成了一半的工作。以下是部分原始碼,建立了 TopologyBuilder,其中包括一個 KeyValueStore

 TopologyBuilder builder = new TopologyBuilder();

 JsonSerializer<StockTransactionSummary> stockTxnSummarySerializer = new JsonSerializer<>();
 JsonDeserializer<StockTransactionSummary> stockTxnSummaryDeserializer = new JsonDeserializer<>(StockTransactionSummary.class);
 JsonDeserializer<StockTransaction> stockTxnDeserializer = new JsonDeserializer<>(StockTransaction.class);
 JsonSerializer<StockTransaction> stockTxnJsonSerializer = new JsonSerializer<>();
 StringSerializer stringSerializer = new StringSerializer();
 StringDeserializer stringDeserializer = new StringDeserializer();


 builder.addSource("stocks-source", stringDeserializer, stockTxnDeserializer, "stocks")
        .addProcessor("summary", StockSummary::new, "stocks-source")
        .addStateStore(Stores.create("stock-transactions").withStringKeys()
                .withValues(stockTxnSummarySerializer,stockTxnSummaryDeserializer).inMemory().maxEntries(100).build(),"summary")
        .addSink("sink", "stocks-out", stringSerializer,stockTxnJsonSerializer,"stocks-source")
        .addSink("sink-2", "transaction-summary", stringSerializer, stockTxnSummarySerializer, "summary");


 System.out.println("Starting KafkaStreaming");
 KafkaStreams streaming = new KafkaStreams(builder, streamingConfig);
 streaming.start();
 System.out.println("Now started");

這段程式碼在建立序列化、非序列化和拓撲構造器方面並無不同。但是有一點不同。第13、14行建立了一個記憶體儲存區(命名為“summary”),供處理器使用。傳到 Stores.create 方法的名字跟前面在處理器 init 方法中用來重寫儲存區的名字一樣。在指定鍵值時,可以使用便捷的 Stores.withStringKeys() 方法,因為 Strings 本來就是它支援的型別,不需要提供引數。但是因為使用了型別化的值,所以使用了 withValues 方法,並提供了序列化和非序列化例項。

用範例程式碼執行處理器

本文所示範例可與實時 Kafka 群集進行對比。指導說明參見本文的 github 資源庫

結論

目前為止,筆者已經介紹了 Kafka 處理器介面的“低層級”部分。希望能讓大家領略到這個新介面能夠給 Kafka 的新老使用者帶來的實用性和多樣性。在下一篇文章中,筆者將會介紹“高層級”的 DSL 介面,以及連線、時窗功能等問題。最後要強調的一點,就是處理器介面和 Kafka 流還在繼續開發中,很快將會有更新。

本文系 OneAPM 工程師整理呈現。OneAPM 能為您提供端到端的應用效能解決方案,我們支援所有常見的框架及應用伺服器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,效能監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格

本文轉自 OneAPM 官方部落格

原文地址:https://dzone.com/articles/introducing-the-kafka-processor-client

相關文章