Kafka Streams入门指南

    应该会有不少人觉得我标题打错了,是不是想讲SparkStreaming或者Kafka+SparkStreaming啊?实际上这不是笔误,Kafka Streams是Kafka 0.10提供的新能力,用于实时处理Kafka中的数据流,和现有的流处理技术如SparkStreaming,Storm,Flink还是有些区别的。

1 概况
    Kafka Streams是一套处理分析Kafka中存储数据的客户端类库,处理完的数据或者写回Kafka,或者发送给外部系统。它构建在一些重要的流处理概念之上:区分事件时间和处理时间、开窗的支持、简单有效的状态管理等。Kafka Streams入门的门槛很低:很容易编写单机的示例程序,然后通过在多台机器上运行多个实例即可水平扩展从而达到高吞吐量。Kafka Streams利用Kafka的并发模型以实现透明的负载均衡。
    一些亮点:
  •     设计成简单和轻量级的客户端类库,可以和现有Java应用、部署工具轻松整合。
  •     除了Kafka自身外不依赖其他外部系统。利用Kafka的分区模型来实现水平扩展并保证有序处理。
  •     支持容错的本地状态,这使得快速高效处理一些有状态的操作(如连接和开窗聚合)成为可能。
  •     支持一次一条记录的处理方式以实现低延迟,也支持基于事件时间的开窗操作。
  •     提供了两套流处理原语:高层的流DSL和低层的处理器API。

2 开发
2.1 核心概念
流处理拓扑
  •     “流”是Kafka Streams最重要的抽象,代表了一个无边界的、持续更新的数据集。流是一种有序的、可回放的、容错的、不可变的数据记录序列,“数据记录”指一个键值对。
  •     一个流处理应用程序通过一或多个“处理器拓扑”来定义其计算逻辑,一个处理器拓扑就是一张以流处理器(节点)和流(边)构成的图。(实际为DAG,太熟悉了吧)
  •     “流处理器”是处理器拓扑中的节点,表示一个转换流中数据的处理步骤,它从上游处理器一次接受一条输入记录,操作记录,然后输出一或多条输出记录到下游处理器。
    Kafka Streams提供两种定义流处理拓扑的方式(上面已提到):流DSL提供最常用的数据变换操作如map和filter;低层的处理器API允许随意连接自定义处理器并与“状态仓库”交互。

时间
    时间的概念在流处理中很关键,比如开窗这种操作就是根据时间边界来定义的。
    上面也提到过两个常见概念:
  •     事件时间:事件或数据记录发生的时刻。
  •     处理时间:事件或数据记录被流处理应用开始处理的时刻,比如记录开始被消费。处理时间可能比事件时间晚几毫秒到几天不等。
    Kafka Streams通过TimestampExtractor接口给每个数据记录赋一个时间戳。可以根据不同的需要来确定时间戳的实现,如使用数据记录的内置时间戳来实现事件时间的语义,或者打上处理器开始消费的时间来实现处理时间的语义。开发者可以根据业务需求来选择一种。

状态
    一些流处理应用不需要管理状态,这意味着一条消息和另一条消息是独立的。(如Storm,不过需要区分Acker中的“状态”,那个是用来确保单条消息exactly once语义的而不是消息间的)如果管理状态的话可以提供很多比较复杂的流处理应用:如在流中连接、分组或聚合数据记录等。大量有状态的操作方法在流DSL中提供。
    Kafka Streams提供了一种“状态仓库”,可被流处理应用用来存储和查询状态数据。这对实现有状态的操作很关键。每个Kafka Streams使用一或多个状态仓库,可通过API来存取数据。这种状态仓库可以是持久化的键值对、内存中的hashmap、或其他各类数据结构。Kafka Streams对本地状态仓库提供了容错和自动恢复。

2.2 低层处理器API
处理器
    可通过实现Processor接口来自定义处理逻辑,该接口有两个主要方法,process方法会被作用于每条收到的记录,punctuate方法基于时间的流逝周期性地执行。另外,处理器可使用init方法中创建的ProcessorContext实例来维护当前上下文,并使用上下文来调用周期性任务(context().schedule),或将修改的、新的键值对推送给下游处理器(context().forward),或提交当前的处理进度(context().commit),等等。
<span style="font-size:12px;">    public class MyProcessor extends Processor {
        private ProcessorContext context;
        private KeyValueStore kvStore;

        @Override
        @SuppressWarnings("unchecked")
        public void init(ProcessorContext context) {
            this.context = context;
            this.context.schedule(1000);
            this.kvStore = (KeyValueStore) context.getStateStore("Counts");
        }

        @Override
        public void process(String dummy, String line) {
            String[] words = line.toLowerCase().split(" ");

            for (String word : words) {
                Integer oldValue = this.kvStore.get(word);

                if (oldValue == null) {
                    this.kvStore.put(word, 1);
                } else {
                    this.kvStore.put(word, oldValue + 1);
                }
            }
        }

        @Override
        public void punctuate(long timestamp) {
            KeyValueIterator iter = this.kvStore.all();

            while (iter.hasNext()) {
                KeyValue entry = iter.next();
                context.forward(entry.key, entry.value.toStrin
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值