分布式编程工具Akka Streams、Kafka Streams和Spark Streaming大PK

网站建设3年前发布
39 0 0

译者 | 陈峻,审校 | 孙淑娟,众所周知,作为一个事件流平台,Kafka能够松散地驻留在面向消息的中间件(Message-oriented Middleware,MoM)空间里。而被称为Actor模型的Akka,是一个基于响应、容错和消息传递的同步计算过程。,下面,我将和您讨论分布式编程工具Akka Streams、Kafka Streams和Spark Streaming的主要特点、优缺点、以及如何在一个简单的字数统计应用中使用它们。文中,我主要使用Scala来编写代码,所涉及到的框架都带有Java API。,Kafka Steams是一个可以处理数据的客户端库(client library)。此处的客户端库是指,我们所编写的应用程序使用了另一个基础设施(在本例中是Kafka集群)所提供的服务。因此,我们需要与一个集群进行交互,以处理持续的数据流。而数据则需要被表示为键值记录的形式,以易于识别,并被组织成主题形式的持久性事件日志。它们本质上是被复制和写入磁盘的持久数据队列。在该架构中,生产者(producer)应用程序将记录推送到主题中(例如电商需要跟踪订单的每一步);而多个消费者(consumer)应用程序需要以各种方式,读取主题中不同时间点的数据。,此类数据结构的架构不但具有高度分布式和可扩展性的特点,而且具有一定的容错性。由于嵌入了exact-once消息语义,Kafka可以确保发来的每一条记录,都能够到达集群,并且仅写入一次,没有重复。正是由于在一般的分布式系统中极难实现,因此Kafka的该特性非常重要。,从Kafka的组织方式来看,其API允许Java或Scala应用程序,在与Kafka集群进行交互的同时,与其他应用程序并行、独立地使用。这种独立性能够满足在大型应用程序中,分布式且可扩展的服务去独立地使用微服务。,上述代码便是单词计数应用的Kafka Steams表现形式。显然,这段代码相对较“重”,我试着对其进行分解。,Kafka针对性能进行了二进制式的记录存储,也就是我们常说的序列化和反序列化。通过上述语句,我们可以在Scala中实现序列化和反序列化(并行转换器)的自动导入。,上述应用代码的第一部分需要配置待连接的Kafka集群的细节。下面是我用Scala写的API。,接下来,我会使用一个构建器模式(builder pattern),从需要的主题中读取记录的键值对。,然后,我们将操作流中一些功能性操作符集中到一张表里。基于Kafka的stream-table二元性,我们可以对Kafka Steams进行数据表级别的聚合和处理互转。,在转换过程中,我们需要将该数据表转换为数据流,以向其他应用程序提供可能感兴趣的主题。,最后,我们需要设置数据流的起停,否则静态流是不会主动做任何事的。,Kafka Steams的主要优点是:Kafka集群会给您提供高速、高容错性和高可扩展性。同时,Kafka也提供exactly-once的消息发送语义。这对于分布式系统来说意义重大,毕竟许多框架无法提供此类保证,进而会出现数据的重复或丢失。同时,Kafka鼓励使用相同消息总线实现微服务的通信,以便用户有权控制并通过Kafka建立自己的微服务内(inter-microservice)通信协议。,当然,Kafka并非没有缺点。,Akka Streams是一种由Scala写的,为JVM构建的高性能代码库。它实施了Reactive Streams规范(Reactive Manifesto)--响应性、弹性、容错和消息驱动的语义。通过它,您完全可以以无限的数据量和100%控制流的拓扑配置,来处理个人记录。Akka Streams提供Actor模型的并发性,其流式组件构建在异步独立组件之上。,Akka Streams的主要优势在于高度可扩展性和容错性。它提供了一个多功能和简洁的流API,即Scala-based DSL。您可以简单地通过“插入”组件来启动它们。同时,Akka Streams还提供了一个低级别的GraphStage API,以便您可以控制个人特定组件的逻辑。,如上文所述,在Kafka中,您的应用程序通过使用消息总线,成为了Kafka集群的客户端API。而Akka Streams是应用程序在逻辑上不可分割的一部分。您可以将Akka Streams想象为应用程序的循环系统,而Kafka只是外部组织“造血库”罢了。,上述代码是单词计数应用程序的Akka Streams表现形式。该Scala看起来比较简洁,让我们来分解其代码的主要部分:,前3行代码构建了最初的数据来源,并发送异步元素(在本例中为字符串)。,上述代码是计算字数的主要部分,它旨在产生在一个简单的字符串列表。,上述代码实现的是Akka Streams将自己的逻辑,运用到不同的流组。下面展示了它的流式逻辑图。,Stream工作流,下面,我们来查看这段代码:,注意,上述代码中有个非常相似的表示流拓扑的结构。我们只用2行代码便轻松地构造出了任意流式布局,而且它们是完全异步、高速且容错的。,由于Akka Streams是一个Reactive Streams的实现,因此其API提供了极快的速度和高度可扩展性。同时,Akka Streams提供了低级别的GraphStage API,使您能够控制自定义流的逻辑,例如:批处理数据、手动中断、以及重定向数据流等,真可谓一切皆有可能。此外,Akka Streams也可以无缝地连接到带有​​Alpakka Kafka连接器​​的Kafka上。 Akka Streams是作为应用程序的开发库被构建的,因此您不必像Kafka那样去编写客户端API,而只需像任何其他库那样,用它去构建分布式应用程序即可。,Akka Streams的缺点在于它类似流式C++,学习曲线比较陡峭。同时,如果您使用集群的整个套件的话,会发现Akka Streams的扩展并不容易。事实上,正是因为Akka Streams成为了应用程序不可分割的一部分,因此您需要像任何“构建”库那样,去采取特定的思维方式。,作为大规模Spark分布式计算引擎的自然流扩展,Spark Streaming的目的是处理持续大规模的数据。目前,您有两个API级别可供选择:一个是带有离散流(Discretized Streams,DStreams)的低级别高可控API,另一个是常见的DataFrame API。它也被称为结构化流,针对常规“静态”大数据,提供了一个相似的API。Spark通过原生的可扩展性和容错性,提供了两种输出模式和功能:,Spark的主要优势体现在大数据的处理能力上。由它提供的DataFrame、SQL API、以及丰富的Spark UI,都能够方便您监视和跟踪负载的实时性能。,值得注意的是,由于Spark需要一个专门的计算集群,因此它在生产环境中比较耗费资源。当然,Spark具有可配置性,如果您知道如何正确地调整它的话,可以在其性能上改进不少。,上述代码便是单词计数应用的Spark Streaming表现形式。在此,我们使用了高级别的结构化流式(Structured Streaming)API,使得代码既整洁又分离。下面,我们来进一步分析:,上述代码只需要您启用一个样板—Spark Session。,由上述代码可知,您可以通过指定数据源来读取数据。同时,Spark Streaming也能够原生地以开箱即用的方式支持Kafka。,上述代码的逻辑也比较简单,在SQL中我们只需运用“group by”来计数。而由于Kafka是以二进制来存储数据的,因此我们必须添加如下头部。,最后,您只需要将数据流指向输出sink(在此我们又用到了Kafka),便可以开始查询数据流了。,Spark具有基于事件时间和水印的数据后期处理能力。这在真实场景下非常实用。同时,高度可配置的Spark,可以通过其内置的连接器,作为数据的输入或输出,连接到Kafka处,来实现性能调优。当然,Spark也拥有优秀的文档和广泛的社区支持。此外,Spark还能够针对较小的数据处理,在本地进行加速。,与其他框架一样,Spark也并不完美。除了通用的DataFrame和SQL API之外,它在编译时,会丧失一部分类型的安全性。而在您将Dataset导入lambdas后,其性能也会有所下降。如前所示,Spark Streaming在大数据和micro-batch处理方面表现不错,但是其continuous模式有待改进。最后,由于Spark需要运行一个专门的集群,因此它也会分走一部分的算力。,可见,上述讨论的每一种框架都是针对某些特定的需求而构建的。那么,我们该如何进行选用呢?,陈峻 (Julian Chen),51CTO社区编辑,具有十多年的IT项目实施经验,善于对内外部资源与风险实施管控,专注传播网络与信息安全知识与经验;持续以博文、专题和译文等形式,分享前沿技术与新知;经常以线上、线下等方式,开展信息安全类培训与授课。,原文标题:Comparing Akka Streams, Kafka Streams and Spark Streaming,作者:Daniel Ciocirlan

© 版权声明

相关文章