Kafka Streams簡介
Kafka Streams被認為是開發實時應用程序的最簡單方法。它是一個Kafka的客戶端API庫,編寫簡單的java和scala代碼就可以實現流式處理。
優勢:
- 彈性,高度可擴展,容錯
- 部署到容器,VM,裸機,云
- 同樣適用于小型,中型和大型用例
- 與Kafka安全性完全集成
- 編寫標準Java和Scala應用程序
- 在Mac,Linux,Windows上開發
- Exactly-once 語義
什么是流式計算
一般流式計算會與批量計算相比較。在流式計算模型中,輸入是持續的,可以認為在時間上是無界的,也就意味著,永遠拿不到全量數據去做計算。同時,計算結果是持續輸出的,也即計算結果在時間上也是無界的。流式計算一般對實時性要求較高,同時一般是先定義目標計算,然后數據到來之后將計算邏輯應用于數據。同時為了提高計算效率,往往盡可能采用增量計算代替全量計算。
為什么要有Kafka Stream
當前已經有非常多的流式處理系統,最知名且應用最多的開源流式處理系統有Spark Streaming和Apache Storm。Apache Storm發展多年,應用廣泛,提供記錄級別的處理能力,當前也支持SQL on Stream。而Spark Streaming基于Apache Spark,可以非常方便與圖計算,SQL處理等集成,功能強大,對于熟悉其它Spark應用開發的用戶而言使用門檻低。另外,目前主流的Hadoop發行版,如MapR,Cloudera和Hortonworks,都集成了Apache Storm和Apache Spark,使得部署更容易。
既然Apache Spark與Apache Storm擁用如此多的優勢,那為何還需要Kafka Stream呢?筆者認為主要有如下原因。
第一,Spark和Storm都是流式處理框架,而Kafka Stream提供的是一個基于Kafka的流式處理類庫。框架要求開發者按照特定的方式去開發邏輯部分,供框架調用。開發者很難了解框架的具體運行方式,從而使得調試成本高,并且使用受限。而Kafka Stream作為流式處理類庫,直接提供具體的類給開發者調用,整個應用的運行方式主要由開發者控制,方便使用和調試。
第二,雖然Cloudera與Hortonworks方便了Storm和Spark的部署,但是這些框架的部署仍然相對復雜。而Kafka Stream作為類庫,可以非常方便的嵌入應用程序中,它對應用的打包和部署基本沒有任何要求。更為重要的是,Kafka Stream充分利用了Kafka的分區機制和Consumer的Rebalance機制,使得Kafka Stream可以非常方便的水平擴展,并且各個實例可以使用不同的部署方式。具體來說,每個運行Kafka Stream的應用程序實例都包含了Kafka Consumer實例,多個同一應用的實例之間并行處理數據集。而不同實例之間的部署方式并不要求一致,比如部分實例可以運行在Web容器中,部分實例可運行在Docker或Kubernetes中。
第三,就流式處理系統而言,基本都支持Kafka作為數據源。例如Storm具有專門的kafka-spout,而Spark也提供專門的spark-streaming-kafka模塊。事實上,Kafka基本上是主流的流式處理系統的標準數據源。換言之,大部分流式系統中都已部署了Kafka,此時使用Kafka Stream的成本非常低。
第四,使用Storm或Spark Streaming時,需要為框架本身的進程預留資源,如Storm的supervisor和Spark on YARN的node manager。即使對于應用實例而言,框架本身也會占用部分資源,如Spark Streaming需要為shuffle和storage預留內存。
第五,由于Kafka本身提供數據持久化,因此Kafka Stream提供滾動部署和滾動升級以及重新計算的能力。
第六,由于Kafka Consumer Rebalance機制,Kafka Stream可以在線動態調整并行度。
簡單編程
public class SpringBootKafkaStreamApplication1 {
public static void main(String[] args) {
Properties props = new Properties();
//定義消費組
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-application");
//定義kafka地址
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "47.105.194.139:9092");
//key編碼方式
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
//value編碼方式
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
//構建輸入流
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> textLines = builder.stream("test5");
//構建輸出table
KTable<String, Long> wordCounts = textLines
// Split each text line, by whitespace, into words.
.flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
// Group the text words as message keys
.groupBy((key, value) -> value)
// Count the occurrences of each word (message key).
.count();
//存入輸出topic
wordCounts.toStream().to("test7", Produced.with(Serdes.String(), Serdes.Long()));
//啟動
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}
創建名為test5、test7的topic。
生產代碼
[root@izm5e11cqeaucml4d3vumbz kafka_2.12-2.4.0]# bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test5
>all streams lead to kafka
>hello kafka streams
>join kafka summit
消費代碼test5
[root@izm5e11cqeaucml4d3vumbz kafka_2.12-2.4.0]# bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test5 --from-beginning
all streams lead to kafka
hello kafka streams
join kafka summit
消費代碼test7
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test7 --from-beginning --formatter kafka.tools.DefaultMessageFormatter --property print.key=true --property print.value=true --property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer --property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer
all 1
lead 1
to 1
hello 1
kafka 2
streams 2
join 1
kafka 3
summit 1
參考資料:
最簡單流處理引擎——Kafka Streams簡介
Kafka設計解析(七)- 流式處理的新貴Kafka Stream
https://www.cnblogs.com/hklv/p/10692999.html
Kafka Streams:Kafka原生計算的基石
教程:編寫Kafka Streams應用程序
https://kafka.apache.org/24/documentation/#producerapi
https://kafka.apache.org/24/documentation/streams/developer-guide/dsl-api#transform-a-stream