Kafka Streams 原理与代码实践

Kafka Streams适用于构建应用程序和微服务的客户端库,其中输入和输出数据存储在kafka集群中。它结合了在客户端编写和部署标准Java和Scala应用程序的简便性,以及kafka服务器端集群技术的优势。
概述:

		   1)kafka Streams类库用于构建实时计算处理应用
		   2)输入和输出来源于kafka
		   3)编程语言支持Java和Scala
		   4)集结kafka服务器端集群技术优势(可靠,容错等特点)

核心概念:
·Topology(拓扑任务):代表是一个流处理计算的任务,在一个拓扑任务中可能包含了多个数据处理逻辑。
·Stream(数据流):代表是一个无限的,不断更新的Record的数据集。
·States(状态):代表的流处理计算的中间结果,状态数据可以用于结果累积和状态恢复容错。
·Processor(处理器):代表的是拓扑任务中的一个处理逻辑:Topology由1到N和Processor构成。
·Time(时间)

事件时间:数据的产生时间
摄入时间:流数据应用将流数据拉取到应用的时间。
处理时间:流数据应用处理数据的时间。
结论:事件时间<=摄入时间<=处理时间

开发简单应用:
导入依赖:

<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-streams -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-streams</artifactId>
    <version>2.3.0</version>
</dependency>
public class WordCountApplication {
    public static void main(String[] args) {
        // 1. 准备配置对象
        Properties prop = new Properties();
        //对应的端口,记得在windows做主机映射
        prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode:9092");
        prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());  // 构建Key序列化和反序列化器
        prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass()); // 构建Value序列化和反序列化器
        prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount"); // 应用标识 以后会作为消费组的ID


        // 2. 构建流式数据源
        // topic input:英文短语 Hello World | Hello Kafka
        // topic output: Hello 2 | World 1 | Kafka 1
        StreamsBuilder streamsBuilder = new StreamsBuilder();
        KStream<String, String> stream = streamsBuilder.stream("input");

        // 3. 应用处理逻辑
        // Hello World --> Hello | World --> 分区 --> 计数
        stream
                // 展开
                .flatMap(new KeyValueMapper<String, String, Iterable<KeyValue<String, String>>>() {
                    public Iterable<KeyValue<String, String>> apply(String key, String value) {
                        ArrayList<KeyValue<String, String>> keyValues = new ArrayList<KeyValue<String, String>>();
                        if (value != null) {
                            String[] words = value.split("\\s");
                            for (String word : words) {
                                keyValues.add(new KeyValue<String, String>(key, word)); // key line => key word
                            }
                        }
                        return keyValues;
                    }
                })
                // 映射(word,1) k=null v=word
                .map(new KeyValueMapper<String, String, KeyValue<String, Long>>() {
                    public KeyValue<String, Long> apply(String key, String value) {

                        return new KeyValue<String, Long>(value, 1L); // Hello 1L
                    }
                })
                // 分组 根据单词进行分组
                .groupByKey(Grouped.with(Serdes.String(), Serdes.Long()))
                // 聚合操作
                // 统计key相同value个数  返回结果表对象
                .count(Materialized.<String, Long, KeyValueStore<Bytes, byte[]>>as("state"))
                .toStream()
                .print(Printed.<String, Long>toSysOut());


        // 4. 构建拓扑任务
        KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(), prop);

        // 5. 启动流式计算
        kafkaStreams.start();
    }
}

原理剖析
在这里插入图片描述
Kafka Streams任务的并行度
Kafka Steam基于应用程序的输入流分区创建固定数量的Task,每个任务(Task)分配来自输入流的分区列表(即Kafka主题)。分区到任务的分配永远不会改变,因此每个任务都是应用程序的固定平行单元。然后,任务可以根据分配的分区实例化自己的处理器拓扑,他们还为每个封面配的分区维护一个缓冲区,并从这些记录缓冲区一次一个的处理消息。因此,流任务可以独立并行地处理,无需人工干预。
用户可以启动多个KafkaStream实例,这样等价启动了多个Stream Tread,每个Thread处理1~n个Task。一个Task对应一个分区,因此Kafka Stream流处理的并行度不会超越Topic的分区数。需要值得注意的是Kafka的每个Task都维护着自身的一些状态,线程之间不存在状态共享和通信。因此Kafka在实现流处理的过程中扩展是非常高效的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值