架构风格之数据流风格

《架构风格之系统结构风格》系统结构风格主要面向组件之间存在相互调用关系的系统,这些系统内部通过应用系统结构相关风格可以降低系统复杂性。而本节介绍的数据流风格则从另一种系统构建需求切入,有些系统内部虽然同样存在各种组件,但这些组件之间各自独立,并不存在相互调用的关系。系统的行为由数据流控制,这种系统普遍存在于数据处理领域。

1. 顺序批处理风格

随着离线大数据技术的兴起,顺序批处理(Sequential Batch)风格得到了广泛应用。在顺序批处理系统中,各个组件互相独立,并且这些组件按照先后顺序处理,仅当一个组件运行彻底结束之后,下一个组件才能开始执行。而且在各个组件之间传输的数据是成批(Batch),而不是以数据流的方式运行,所以称之为顺序批处理。一个典型的离线批量处理系统如下图所示,这是一个典型的ETL(Extract-Transform-Load,抽取-转换-装载)系统,我们可以看到每一个组件之间传输的是批量数据,这些批量数据以文本文件为存储媒介。

顺序批处理风格中,每个组件都是互为独立的程序,只有上一步组件完成之后下一步组件才能开始。同时,数据作为一个整体批量传输。因为每个组件职能顺序进行,其性能取决于其包含的所有组件的处理时间总和。所以,以上特点决定了其不适合应用于实时数据处理,而往往面向海量数据的离线处理场景。

2. 管道-过滤器风格

管道-过滤器(Pipe-Filter)风格代表另一种面向数据流的处理方式。顾名思义,管道-过滤器结构主要包括过滤器和管道两种元素(见下图)。其中功能组件被称为过滤器(Filter),负责对数据进行加工处理。每个过滤器都有一组输入端口和输出端口,从输入端口接收数据,经过内部加工处理之后,传送到输出端口上。数据通过相邻过滤器之间的连接件进行传输,管道(Pipe)可以看作输入数据流和输出数据流之间的通路。

管道-过滤器结构将数据流处理分为几个顺序的步骤来进行,一个步骤的输出是下一个步骤的输入,每个处理步骤由一个过滤器来实现。每个过滤器独立完成自己的任务,不同过滤器之间不需要进行交互。在管道-过滤器结构中,数据输出的最终结果与各个过滤器执行的顺序无关。这些特性允许将系统的输入和输出看作是各个过滤器行为的简单组合,独立的过滤器能够减小组件之间的耦合程度,也可以很容易地将新过滤器添加到现有的系统之中,原有过滤器可以很方便地被改进的过滤器所替换以扩展系统的业务处理能力。

虽然在管道-过滤器风格中,每个过滤器之间不存在相互调用,即每个组件的处理过程也保持独立,这点与顺序批处理风格一致。但两者在数据处理方式上有本质不同,在顺序批处理风格中,数据以批量方式传输,且只有上一个组件完成之后才能传输到下一组件。但在管道-过滤器风格中,过滤器以流对流的方式变换数据,各个过滤器本质上是在并行工作。

3. 数据流风格应用

(1)顺序批处理风格

为了实现批处理需求,业界也产生了一系列有效的工具和框架,代表性的就是Hadoop生态圈中的相关技术,其他轻量级的实现方案也包含Spring旗下的Spring Batch。顺序批处理风格的特点是下一个组件只有在上一个组件成功完成之后才能启动,健壮性(Robustness)是最核心的非功能性需求,因为长时间、离线处理、自动化等需求促使批处理的过程应该保持在非人工干预下能够有一定的智能化机制。

要想实现智能化,首先需要确保对任务的执行过程可跟踪并在产生失败时进行任务重启。健壮性的实现体现在以下三种策略:Skip即忽略,对于类如文件中某个数字格式错误等非致命异常,我们认为直接忽略这些异常比停止Job更加合适;Retry即重试,对于网络失败或数据库锁等瞬态异常,重试在很大程序上能够确保Job的正常执行;Restart即重启,对于因为数据格式错误太多、业务处理异常等任务执行失败场景,Skip会导致业务逻辑错误而Retry显然也不能解决问题,这时候就需要对问题进行修正后重新执行。以上三种健壮性策略可以组合使用,也可以根据特定业务规则在运行过程中把某种策略动态替换成其他策略,比如,当数据格式小于某个界限时,使用Skip是合适的,但超过这个界限就认为需要进行Restart。

(2)管道-过滤器风格

管道-过滤器风格的典型应用就是Linux命令执行过程。例如在cat input.txt | grep “hello” | sort > output.txt这句命令中,grep命令在文件input.txt中搜索包含“hello”的所有行,并且将它们打印出来,然后sort命令将上一步的结果内容进排序,并通过sort > output.txt命令将排序后的结果输出到output.txt文件中。基于以上命令的管道-过滤器风格的结构图如下,在Linux中,广泛使用类似的方式处理命令。

我们再来考虑下面的例子。设想我们需要解决这样的问题,当输入一串包含大小写的英文字符时,需要把它们全部转换为大写,然后在末尾加一个感叹号。这个问题实现的方法当然有很多,但考虑到后续如果对文字的转换还有其他需求且这些需求存在不断变化的可能性时,作为扩展性架构风格的代表,管道-过滤器就体现出它的优势。如下图所示就是使用管道-过滤器风格解决这个问题的设计方案,可以看到存在通用的Filter和Pipe接口,WriterFilter作为第一个入口Filter衔接CaptialPipe使数据流转到CaptialFilter,而CaptialFilter完成了把输入文字转变成大写的过程;同样,大写文字经由ExclamationPipe到ExclamationFilter并实现在末尾加一个感叹号的效果。我们可以根据这个风格体系构建出任意多个Filter和Pipe并组装到整个管道-过滤器链中,各个Filter和Pipe相互独立且又共同协作完成复杂业务逻辑。

如果对文章感兴趣,可以关注我的微信公众号:程序员向架构师转型,或扫描下面的二维码。

我出版了《系统架构设计:程序员向架构师转型之路》、《向技术管理者转型:软件开发人员跨越行业、技术、管理的转型思维与实践》、《微服务设计原理与架构》、《微服务架构实战》等书籍,并翻译有《深入RabbitMQ》和《Spring5响应式编程实战》,欢迎交流。

发布了92 篇原创文章 · 获赞 9 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/99001800