Confluent Inc(原LinkedIn Kafka做者离职后创业公司)在6月份预告推出Kafka Stream,Kafka Stream会在Kafka 0.10版本中推出。数据库
对于流计算,已经有Storm、Spark,Samza,包括最近新起的Flink,Kafka为何再本身作一套流计算呢?Kafka Stream 与这些框架比有什么优点?Samza、Consumer Group已经包装了Kafka轻量级的消费功能,难道不够吗?编程
花了一些时间阅读docs 和一些PPT,写一份粗略的调研材料供你们参考。windows
Single:例如HTTP,发送一个Request请求、返回一个Response安全
Batch:将一组做业提交给计算机,返回一组,优点是减小IO等待时间服务器
Stream:Batch异步过程,任务和任务之间没有明显的边界多线程
以wordcount来做例子,咱们能够启动一个server,内存中创建一个HashMap,把输入先分词,而后根据word视图更新HashMap。是否是很简单?但带来的问题是什么?架构
若是挂了,数据都被清空,数据重复怎么办?
若是数据量很是大,一块内存放不下怎么办?
若是在多台机器上部署,如何保证分配策略和前后顺序?
咱们把这些问题作一个分类,主要有这样几个:负载均衡
保序处理
规模和切片
异常恢复
状态类计算(例如TopK,UV等)
从新计算
时间、窗口等相关问题框架
比较成熟度的框架有:Apache Spark, Storm(咱们公司开源Jstorm), Flink, Samza 等。第三方有:Google’s DataFlow,AWS Lambdaless
现有框架的好处是什么?
强大计算能力,例如Spark Streaming上已经包含Graph Compute,MLLib等适合迭代计算库,在特定场景中很是好用。
问题是什么?
使用起来比较复杂,例如将业务逻辑迁移到完备的框架中,Spark RDD,Spout等。有一些工做试图提供SQL等更易使用模式下降了开发门槛,但对于个性化ETL工做(大部分ETL实际上是不须要重量级的流计算框架的)须要在SQL中写UDF,流计算框架就退化为一个纯粹的容器或沙箱。
做者认为部署Storm,Spark等须要预留集群资源,对开发者也是一种负担。
Kafka Stream定位是轻量级的流计算类库,简单体如今什么方面?
全部功能放在Lib中实现,实现的程序不依赖单独执行环境
能够用Mesos,K8S,Yarn和Ladmda等独立调度执行Binary,试想能够经过Lamdba+Kafka实现一个按需付费、并能弹性扩展的流计算系统,是否是很cool?
能够在单集成、单线程、多线程进行支持
在一个编程模型中支持Stateless,Stateful两种类型计算
编程模型比较简洁,基于Kafka Consumer Lib,及Key-Affinity特性开发,代码只要处理执行逻辑就能够,Failover和规模等问题由Kafka自己特性帮助解决
我的感受Kafka Lib是Samza一个加强版(Samza也是Linkedin与Kafka深度集成的流计算框架),未来能够替换Samza,但没法撼动Spark、Flink等语义上比较高级的流计算系统地位,只能作一些轻量级流处理的场景(例如ETL,数据集成,清洗等)。
先来看一个例子,经过Kafka Stream代码开发:
这里面作了这样几件事情:
构建了Kafka中数据序列化/反序列化方式
构建了2个计算节点
分词(flatMapValues),并将结果根据Key来Map
Reduce(根据Key来计算结果)
将结果写到Kafka一个结果Topic中(增量方式)
在2个结算节点中,使用了一个Kafka Topic将计算结果序列化、并反序列化。至关于Map-Reduce中Streamline。
这段程序能够执行在一个Thread中,也能够执行在N台机器上,主要归结于Kafka Consumer Lib能够帮助对数据与计算解耦分离。
Processor:Processor是一个基本的计算节点
void process (K key, V Value); void punctuate(long time stampe); }
Stream: Processor 处理后后结果输出
二者的关系如图:
对Kafka而言,在一个Partition(Shard)下,数据是先进先出严格有序的,所以不是问题。
流计算规模取决于2个因素:数据是否能线性扩容、计算可否线性扩容。
Kafka中的数据经过Partition方式划分,每一个Partition严格有序,能够作到弹性伸缩(实际上目前版本中弹性伸缩是不完整的,Kafka在0.10版本中能提供彻底弹性伸缩的能力)。
Kafka对于消费端提供Consumer Group功能,能够扩展消费Instance达到与Partition一样的水平扩展能力,过程当中保证一个消费Instance只能消费一个Partition。
Kafka Consumer Group已实现了负载均衡,所以当有消费实例crash时也能保证迅速未完成的任务,过程当中数据不丢,可能会重复(取决于消费checkpoint配合)
这个问题相对比较复杂,在流计算场景中,分为两类计算:
Stateless(无状态):例如Filter,Map,Joins,这些只要数据流过一遍便可,不依赖于先后的状态
Stateful(有状态):主要是基于时间Aggregation,例如某段时间的TopK,UV等,当数据达到计算节点时须要根据内存中状态计算出数值
Kafka Stream 提供了一个抽象概念KTable,KStream来解决状态存储和数据变化的问题,见下面的章节解释。
在了解了RedoLog和State后,重放这个概念并不难理解
时间是流计算的一个重要熟悉,由于在现实过程当中数据采集每每并非很完美的,历史数据的到来会打断咱们对计算的假设。时间有两个概念:
Event Time: 物理时间中的客观时间,表明事件发生时的一刻
Processing Time: 实际处理的时间(到达服务器时间)
虽然Processing Time对处理比较容易,但因历史数据的影响,采用Event Time更为准确。一个零售业中比较典型的场景是:统计每10分钟内每一个产品的销量(或网站每一个时间点UV、PV的统计)。销售数据可能会从不一样的渠道实时流入,所以咱们必须依赖于销售数据产生的时间点来做为窗口,而不是数据达到计算的点。
Kafka Stream用一种比较简单粗暴方式来解决这个问题,他会给每一个windows一个状态,这个状态只是表明当前时刻的数值,当有新数据达到该窗口时,状态就被改变了。对于windows based aggregation,Kafka Stream作法是:
Table (状态数据) + Library = Stateful Service
为了实现状态的概念,Kafka 抽象了两种实体Kstream, KTable
Stream 等同于数据库中Change log
Table 等同于数据库在一个时间点Snapshot,两个不一样的Snapshot之间经过1个或多个changelog形成
假设有2个流,一个流是送货,另一个流是销售,咱们对着两个流进行Join,得到当前的库存状态:
shipment stream:
item ID | store code | count |
---|---|---|
10 | CA | 200 |
23 | NY | 50 |
23 | CA | 101 |
54 | WA | 1000 |
sale stream:
item ID | store code | count |
---|---|---|
10 | CA | 20 |
23 | NY | 10 |
当这两个流中的记录前后达到状况下,会影响库存状态,整个库存的变化状态以下:
咱们把这两个流放到Kafka Stream中,就会看到一个Processor节点中的状态变化以下:
基于状态数据,咱们能够在该节点定义处理的逻辑:
if (state.inventory[item].size < 10) { notify the manager; } else if (state.inventory[item] > 100) { on sale; }
KTable,KStream可能比较抽象,KafkaStream包装了high-level DSL,直接提供了filter, map, join等算子,固然若是有个性化需求可使用更低抽象程度API来完成。
流计算场景中,是否会有两个极端:复杂内存操做+迭代计算,轻量级数据加工与ETL。这两个比例分别占据多少?在咱们经常使用的ETL场景里,大部分实际上是轻量级Filter,LookUP,Write Storage等操做,有时候咱们为了对数据作加工,不得不借助一个执行容器去选择流计算的框架。Docker,Lamdba能够解决这类问题,但须要有必定流计算的开发量。
我以为对轻量级ETL场景,一个而理想的架构是Kafka Stream这样的轻量级计算库+Lamdba,这样就能作到安全按需使用的流计算模式。
Kafka Stream有一些关键东西没有解决,例如在join场景中,须要保证来源2个Topic数据Shard个数必须是必定的,由于自己作不到MapJoin等技术。在以前的版本中,也没有提供EventTime等Meta字段。