• 到底什么是流处理?
  • 发布于 1个月前
  • 58 热度
    0 评论
  • Jeff
  • 1 粉丝 43 篇博客
  •   
流处理就是数据处理工作流,本质上是一种计算机编程范例。非要做一个定义,那就是流处理是对接收到的新数据事件的连续处理。‌它涉及对从生产者到消费者的一系列事件进行处理。‌咱们从大数据、流式编程、Java流三个方面来聊。但是首先咱们先看看什么是数据流。

什么是数据流
数据流(也被称为事件流或流数据),是无边界数据集的抽象表示。无边界意味着无限和持续增长。无边界数据集之所以是无限的,是因为随着时间的推移,会有新记录不断加入。谷歌和亚马逊等大多数公司采用了这个定义。这个简单的模型(事件流)几乎可以用来表示任何一种业务活动,比如信用卡交易、股票交易、包裹递送、流经交换机的网络事件、制造商设备传感器发出的事件、发送出去的邮件、游戏场景中的物体移动,等等。这样的例子不胜枚举,因为大部分事情可以被看成一个事件序列。

除了无边界,事件流模型还有其他一些属性:
事件流是有序的
事件的发生都有先后顺序。以金融活动事件为例,先把钱存进账户,然后再把钱花掉与先把钱花掉,然后再把钱存入账户的顺序是完全不一样的。后者会出现透支,前者则不会。

不可变的数据记录
事件一旦发生,就不能被改变。一次金融交易被取消,并不是说它消失了,相反,表示前一个交易操作被取消的事件将被添加到事件流中。顾客向商店退货,之前的销售事实并不会消失,退货行为将被视为一个额外的事件。

事件流是可重放的
这是事件流非常有价值的一个属性。我们都知道不可重放的流(流经套接字的TCP数据包通常是不可重放的),但对大多数业务应用程序来说,能够重放发生在几个月前(甚至几年前)的原始事件流是非常关键的。可能是为了能够使用新的分析方法纠正过去的错误,或者是为了达到审计的目的。

“流式处理”的真正含义
流式处理是指实时地处理一个或多个事件流。流式处理是一种编程范式,就像请求与响应范式和批处理范式一样。下面将对这3种范式进行比较,以便更好地理解如何在软件架构中应用流式处理。

请求与响应
这是延迟最小的一种范式,响应时间在亚毫秒和毫秒之间,通常也比较稳定。这种处理模式一般是阻塞的,即应用程序会向处理系统发出请求,然后等待响应。在数据库领域,这种范式就是联机事务处理(OLTP)。销售点(POS)系统、信用卡处理系统和基于时间的追踪系统通常都使用这种范式。

批处理
这种范式具有高延迟和高吞吐量的特点。处理系统按照设定的时间启动处理进程,比如每天凌晨两点开始启动、每小时启动一次,等等。

流式处理
这种范式是连续的、非阻塞的。流式处理填补了请求与响应范式和批处理范式之间的空白。

大多数业务流程是持续进行的,只要业务报告保持更新,业务产品线应用程序能够持续响应,处理流程就可以进行下去,不一定需要毫秒级的响应。具有持续性和非阻塞特点的业务流程,比如针对可疑信用卡交易或网络发送告警、根据供应关系实时调整价格、跟踪快递包裹,都可以选择这种范式。

大数据领域
在大数据领域,常见的流处理框架,有Apache Flink、Apache Storm、Apache Spark Streaming、Kafka Streams等。

都是流处理框架,flink和kafka有什么区别?
Kafka是一种分布式流处理平台,用于在分布式系统之间可靠地传输数据流。它是一种可扩展的、高吞吐量的发布-订阅消息系统,具有高效、持久和可靠的消息传递特性。Kafka被广泛用于构建实时数据管道和流处理应用程序,可处理来自多个数据源的数据流,并将数据流转换为有用的数据。

Flink是一种实时数据流处理引擎,具有高吞吐量、低延迟、高可用性等特性,它支持在流数据和批处理之间无缝切换。Flink可以进行实时的ETL(Extract-Transform-Load)操作、流式查询和机器学习。Flink提供了丰富的API和开箱即用的库,可以方便地构建、部署和管理大规模的实时数据流处理应用程序。

因此,Kafka和Flink的主要区别在于其定位和功能。Kafka主要用于数据传输和实时数据管道的构建,而Flink主要用于实时数据流处理和流式计算。

流式编程
刚才提到的流式计算,没实际用过MapReduce的话可能感触不深。但流式编程与MapReduce本质是一样的。Stream(流)是 Java 8 引入的一个新的抽象概念,它代表着一种处理数据的序列。简单来说,Stream 是一系列元素的集合,这些元素可以是集合、数组、I/O 资源或者其他数据源。

Stream API 提供了丰富的操作方法,可以对 Stream 中的元素进行各种转换、过滤、映射、聚合等操作,从而实现对数据的处理和操作。Stream API 的设计目标是提供一种高效、可扩展和易于使用的方式来处理大量的数据。

Java的流式编程主要通过Stream API来实现,主要包括以下三个步骤:
创建流:可以通过集合类的stream()方法或Stream类的静态方法来创建流,如List.stream()、Stream.of()等。
中间操作:可以使用一系列的中间操作来对流中的元素进行处理,如filter()、map()、sorted()等。
终端操作:最后需要使用一个终端操作来触发流的计算并获取结果,如forEach()、collect()、reduce()等。

本质上对应了文章一开始的生产者、工作流处理和消费者三个过程。

Java流

使用文件输入流和文件输出流进行文件的读取和写入。
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");
        // 堆代码 duidaima.com
        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;
        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            // 打开阀门1,流转数据(输入端)
            int data = in.read();
            // 打开阀门2,流转数据(输出端)
            out.write(data);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
上述的文件复制效率很低,每复制一个字符,都要开启关闭阀门一次,因此,java也提供了缓冲区的优化方式。它的概念非常容易,就是在文件传输的管道中,增加了一个缓冲管道。

import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;

        // 缓冲输入流
        BufferedInputStream bufferIn = null;
        BufferedOutputStream bufferOut = null;

        // 创建一个大小为1024字节的缓存区cache。这将用于存储从源文件读取的数据,然后再写入目标文件。
        byte[] cache = new byte[1024];

        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            // 缓冲输入流
            bufferIn = new BufferedInputStream(in);
            // 缓冲输出流
            bufferOut = new BufferedOutputStream(out);

            // 数据流转
            int data;
            // 当读取到文件末尾(即data为-1)时,循环结束。
            while ((data = bufferIn.read(cache)) != -1){
                bufferOut.write(cache,0,data);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(bufferIn != null){
                try {
                    bufferIn.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(bufferOut != null){
                try {
                    bufferOut.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
从Java流中可以很明显的感受到流的作用:我们不生产数据,我们只是数据的流水线。
用户评论