精华内容
下载资源
问答
  • 2017-01-23 10:14:56


    1.  什么是流处理

    一种被设计来处理无穷数据集的数据处理系统引擎

    2.  流处理的几个概念

    1.     无穷数据(Unbounded data):一种持续生成,本质上是无穷尽的数据集。它经常会被称为“流数据”。然而,用流和批次来定义数据集的时候就有问题了,因为如前所述,这就意味着用处理数据的引擎的类型来定义数据的类型。现实中,这两类数据的本质区别在于是否有限,因此用能体现出这个区别的词汇来定性数据就更好一些。因此我更倾向于用无穷数据来指代无限流数据集,用有穷数据来指代有限的批次数据。

    2.     无穷数据处理(Unbounded dataprocessing):一种发展中的数据处理模式,应用于前面所说的无穷数据类型。尽管我本人也喜欢使用流式计算来代表这种类型的数据处理方式,但是在本文这个环境里,这个说法是误导的。用批处理引擎循环运行来处理无穷数据这个方法在批处理系统刚开始构思的时候就出现了。相反的,设计完善的流计算系统则比批处理系统更能承担处理有穷数据的工作。因此,为了清晰明了,本文里我就只用无穷数据处理。

    3.     低延迟,近似和/或推测性结果(Low-latency,approximate,and/or speculative results):这些结果和流处理引擎经常关联在一起。批处理系统传统上不是设计来处理低延迟或推测性结果这个事实仅仅是一个历史产物,并无它意。当然,如果想,批处理引擎也完全能产生近似结果。因此就如其他的术语,最好是用这些术语是什么来描述这些结果,而不是用历史上它们是用什么东西(通过流计算引擎)产生的来描述。

    3.  流处理的六种方式

    3.1.  Event Sourcing

    Event Sourcing是由老马(Martinfowler)提出来的一种模式,可以解释为事件溯源,该模式从事件源开始保存应用程序状态,保证每次更改都可以被保存为事件序列,也就是说,它不仅保存时间的自身,还保存时间的所有状态变化,在有需要的时候,可以随时根据时间日志重建当时状态,即不仅能做到知道在哪里,也能做到知道如何到那里去的,它会完整的描述对象的整个生命周期中经历的所有事件。

    这方面典型的架构就是LMAX。

    3.2.  Reactive

    反应式编程,也称为响应式编程。顾名思义,就是根据用户的输入来做出响应。这种动作一般是实时的。

    根据响应式编程宣言所述,反应式变成一般具有如下几个特点:

    响应:响应性是系统高可用的基础,但更重要的是,响应性意味着可以快速检测问题并处理问题。响应系统专注于提供快速一致的响应时间,建立可靠的上限,以便提供一致的服务质量,从而简化错误处理,建立用户信息,并鼓励做出更进一步的交互。

    弹性:系统在面对失败时候,仍然保持响应。

    弹性:系统在负载变化的时候仍然保持响应。我们可以通过增加或者减少分配给这些服务的资源来对负载变化做出响应。这意味着整个设计没有竞争和单点瓶颈。

    消息驱动:响应式系统依靠异步消息在组件之间建立一个边界,确保组件之间的松耦合和隔离性。这个边界还提供了失败委托,负载均衡,弹性和流控等手段来保证系统的高可用性,这是响应式系统的一个必备特点。

    3.3.  CEP

    Complex event processing。复杂事件处理。

    事件即事物的状态信息变化,事物之间的作用的动作。复杂事件处理描述的就是系统如何持续地处理这些事件,对系统对变化的持续反应。不论是个体还是系统,都需要从大量的实践中过滤提取,按照既定的处理反应规则做处理。CEP主要依靠规则语言或者持续查询语言来完成事件的过滤、判断和处理。

    这类典型应用比如Esper,TIBCO,IBM Streams等。

    3.4.  Stream Processing

    这个流处理架构,从大数据量领域发展起来的实时数据处理模型,其主要强调分布式,高性能,高可靠性。目前主要有Storm,Flink,Spark Streaming等,这类介绍的比较多,这里就不详细介绍了。

    3.5.  Actors/SEDA

    SEDA(Staged event-driven architecture) 阶段事件驱动架构,也成为阶段是服务器模型,其主要是将复杂的,事件驱动的应用分解为一系列通过队列连接的阶段,从而避免线程的并发模型带来高负载问题,同时还可以达到解耦,负载均衡,分布式等特性。

    Actor模型包装了消息传输和封装机制,用户只需要面对消息和业务逻辑,因此天然就具有分布式、高并发、无状态的特性;对外提供简单编程接口。

    这类应用,典型的有Apache Gearpump(基于Akka),Kafka Streams等。

    3.6.  Change Capture

    变更数据捕获,捕捉数据库插入、更新和删除的动作并作出相应反应。

    目前很多数据库都提供这样的功能,能够将数据操作日志导出并可以由其他工具导入Kafka等系统中来做二次处理。比如Kafka,Mysql等都提供这样的功能。

    4.  流处理的发展目标

    但是Kafka Streams的作者提出了一个观点,个人非常赞同,

     

    So what did we learn?Lots. One of the key misconceptions we had was that stream processing would beused in a way sort of like a real-time MapReduce layer. What we eventually cameto realize, though, was that the most compelling applications for streamprocessing are actually pretty different from what you would typically do witha Hive or Spark job—they are closer to being a kind of asynchronousmicroservice rather than being a faster version of a batch analytics job.

    What do I mean by this? What Imean is that these stream processing apps were most often software thatimplemented core functions in the business rather than computing analyticsabout the business.

     

    翻译过来的核心观点就是:

    我们曾经有过的一个关键的错觉是以为流处理将会被以一种类似于实时的MapReduce层的方式使用。我们最终却发现,大部分对流处理有需求的应用实际上和我们通常使用Hive或者Spark job所做的事情有很大不同,这些应用更接近于一种异步的微服务,而不是批量分析任务的快速版本

    大部分流处理程序是用来实现核心的业务逻辑,而不是用于对业务进行分析

    流处理当前需要在如下几个方面进行进入以保证自己的核心竞争力。

    1.     强一致性:这保证流计算能和批处理平起平坐。

    本质上,准确性取决于存储的一致性。流计算系统需要一些类似于checkpoint的方法来保证长时间的持久化状态。几年前,当Spark刚刚出现在大数据领域的时候,它几乎就是照亮了流计算黑暗面的灯塔(因为Spark支持强一致)。在这之后,情况越来越好。但是还是有不少流计算系统被设计和开发成尽量不去支持强一致性。目前Flink,Kafka Streams也能够支持强一致性,这简直就是流处理的福音。

    再次强调一遍重点:强一致性必须是“只处理一次(exactly-onceprocessing)”,这样才能保证正确性。只有这样的系统才能追平并最终超越批处理系统。除非你对计算的结果是否正确并不介意,否则我还是请你放弃任何不能保证强一致性的流计算系统。现有的批处理系统都保证强一致性,不会让你在使用前去检查计算结果是否正确。所以也不要浪费你的时间在那些达不到这样标准的流计算系统上。

    2. 时间推理的工具:这一点让流计算超越批处理。

    在处理无穷的、无序的、事件—时间分布不均衡的数据时,好的时间推理工具对于流计算系统是极其重要的。现在越来越多的数据已经呈现出上面的这些特征,而现有的批处理系统(也包括几乎所有的流计算系统)都缺少必要的工具来应对这些特性带来的难题。

    3、弹性伸缩功能,即在保证Exactly-once 语义的情况下,流处理应用无需用户的介入也能自动修改并发数,实现应用的自动扩容和缩容。

    4、流上的SQL查询功能以及完整SQL支持,包含窗口,模式匹配等语法支持。


    5.  时间

    流处理系统中的时间分为两种:事件时间和处理时间。

    事件时间(Event Time): 事件发生的时间

    处理时间(Processing Time) 事件处理的时间。

    时间是流处理的基础,绝大部分场景都对时间又严格要求。(PS如果没有的话,那这个世界简直就太简单了。)

    Kafka Streams系统中就要求必须以包含时间,如果没有事件时间,就必须在保存的时候添加系统时间。

    我们能够基于时间来对数据做聚合,实现窗口功能,能够解决乱序问题,总之,时间是流处理最为重要的一个因素。

    6.  Lambda架构

    Lambda架构最初是由Storm的创始人NatanMarz在2011年提出的。在他的文章《How to beat the CAP theorem》中提出了Lambda架构,通过流和批的融合,实现快速的实时数据处理,或者说是让批来为流提供服务。

    一个典型的lambada的架构来自于下面的图。


    这在当时是一个影响力很大的架构,并且有很多产品是基于该架构的,但是随着技术的演进,在现在看来,还是有很大问题的。比如:

    1、  lambda架构要搭建部署和维护两套队列的集群,并且对结果做合并,这是十分麻烦的,并且可靠性也是相当差的。

    2、  数据冗余,数据要同时进入两套系统,存放两份。

    7.  乱序问题

    分布式的流处理系统,不可避免的会遇到数据的乱序问题,数据乱序就是指数据达到某一个节点的时候,已经不是按照原来发生的顺序了,期间可能有丢失,错乱等。

             乱序问题一般的处理方式是使用时间排序窗口,不论是系统时间驱动还是事件时间驱动。其实就是数据在进入节点之后,按照时间进行排序,然后等待一段时间,等待事件都已经到达之后再来进行下一步操作。如果无法确定事件都已经到达,或者是由部分时间一直没有达到,那么就等到窗口超时为止,然后计算结果。

             这个时候计算出来的结果,在有的系统里面已经作为最终结果,直接输出了,即使后续时间过了很长时间已经到达了,也是直接丢弃,不会影响最终结果。在另外一些系统中,比如google的MillWheel中,有水印(WaterMark)机制可以在事件最终到来之后,重新计算并刷新结果。

    8.  数据可靠性问题

    流上的可靠性一直是一个老大难问题,在和业界其他人交流的时候,也纷纷摇头,这个问题无解。目前流处理最广泛的应用还是做一些不怎么关注可靠性的计算。

    不过这个问题在2016年有了很大的突破。

    Intel在2016年终于发布了自己的内存快照技术,通过使用新的存储介质,能够达到内存一半的存取速度,这是一个很了不起的成就,已经基本可以商用了,在流计算领域,为了性能考虑,数据都是保存在内存中的,如果操作系统能够自动完成快照,那就很大程度上保证了数据的可靠性,流处理系统就可以完全不用关注这些什么状态,数据等信息,内存里面已经全部都有了。如果Intel能够做到增量式的内存快照,或者是快照速度和内存读取速度一致,甚至更快的时候,流处理系统的春天就会到来,哪个时候,流处理系统就会变得无比简单。

    在硬件得到突破的同时,软件方面也在不断的取得突破。

    Flink的流处理系统实现了CheckPoint功能,能够将窗口数据保存到内存当中,当流计算发生故障的时候,得到快速恢复,目前功能已经可用,不过性能还是会差一些,在百万TPS级别,快照速度在秒级,还是稍微有些慢,不过已经可用。

    Kafka Streams的推出,利用Kafka消息队列自身Offset的特性,再加上新开发的compact功能,成功实现了流和表的结果,并且也可以通过重放来实现可靠性,状态信息通过数据库保存,这也是流处理在可靠性方面的突破,利用外界第三方组件来实现自身的可靠性。

    流处理系统可靠性处理还有一种趋势就是数据库。数据库系统天然具有可靠性和事务性的特点,能够很好的适应金融等事务性和可靠性比较高的场景,唯一就是数据量和拓展性存在问题,但是随着分布式内存数据库等的发展,也许后面分布式内存数据库替代流处理系统也不是不可能的。但是无论怎么说,我们最求的是实时计算,而不是流处理系统,我们的目标是Fast Data。

    9.        流上的SQL

    以前,CEP和其他流处理平台上面的SQL是五花八门,各有各的特点,包括我们的StreamCQL,都属于类SQL的范畴,但是从现在来看,流上的SQL目前发展趋势已经很明显了,那就是兼容标准SQL,语义不能和标准SQL冲突,这个是第一步;然后就是做尽可能少的拓展,尽可能的利用其他数据库SQL中已有的语法,比如Oracle的Match  recognize等语法来实现模式匹配功能,这样用户最容易接受。窗口等特性,使用函数功能或者是复用SQL的 over语法即可。不支持的场景可以适当做少量拓展。总的来说,流式SQL还在拓展阶段,大家都在拼命抢地盘,想占领制高点,然后推广自己的语法。

     

    9.1.    流和表的关系

    流和表的关系是理解流上SQL的基础,也是最重要的

    流和表实际上是一体的。

    流很容易理解,就是一个管道,当有窗口存在的时候,数据才会发生汇聚。

    表就是我们通常理解的数据库的表。

    流上窗口中的数据,实际上就是一张表。同样的,当表上数据在不断发生变化的时候,这种changelog,就是流。

    这种观点现在已经基本成为流处理领域概念理解的标准。而且绝大部分数据库都支持的data capture,也是传统数据库为了和流处理相结合而做出的改变。Kafka Streams就包含data capture的工具,支持将数据从数据库中导入kafka成为一个流。

    9.2.    流和表的转换

    1)        流和计算结果是表,这一点在分组窗口上体现的特别明显。而且我们的使用习惯也和表一致。

    比如:

    我们统计最近10分钟各区域的用户点击次数,输出每个区域的总点击次数,那么一次性就会输出多行结果,每个地区一行记录,这个就是流上的聚合结果,也是流的中间状态;而为了可靠性,我们一般会将这些计算结果保存到数据库中,以便于故障时候的恢复。

    2)        表中每一次的数据变化,用change log体现就是流。

    3)        表和流之间的Join,实际上是窗口和表之间的Join

    10.         参考

    https://www.oreilly.com.cn/ideas/?p=18&from=timeline

    http://www.cnblogs.com/devos/p/5616086.html

    http://www.oreilly.com/data/free/stream-processing.csp

    http://www.martinfowler.com/eaaDev/EventSourcing.html

    http://www.reactivemanifesto.org/

    http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html

     

    更多相关内容
  • JAVA:IO 之 节点处理流(2)

    万次阅读 多人点赞 2017-05-15 15:37:24
    处理数据单位不同:字节,字符。 (1) 字节:数据中最小的数据单元是字节。 (2)字符:数据中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。 按功能不同:节点,...

    微信扫一扫关注该公众号
    在这里插入图片描述

    #1. 流的分类

    • 按数据流的方向不同:输入流,输出流。

    • 按处理数据单位不同:字节流,字符流。
      (1) 字节流:数据流中最小的数据单元是字节。
      (2)字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

    • 按功能不同:节点流,处理流。
      (1)程序用于直接操作目标设备所对应的类叫节点流。
      (2)程序通过一个间接流类去调用节点流类,以达到更加灵活方便地读写各种类型的数据,这个间接流类就是处理流。
      #2. 节点流
      ##2.1 节点流的类型
      这里写图片描述

    • (1)File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream。、

    • (2)Memory
      1)从/向内存数组读写数据: CharArrayReader与 CharArrayWriter、ByteArrayInputStream与ByteArrayOutputStream。
      2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream。

    • (3)Pipe管道流。 实现管道的输入和输出(进程间通信): PipedReader与PipedWriter、PipedInputStream与PipedOutputStream。
      ##2.2 节点流执行的图示
      这里写图片描述
      #3. 处理流
      ##3.1 处理流的类型
      这里写图片描述

    • (1)Buffering缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader与BufferedWriter、BufferedInputStream与BufferedOutputStream。

    • (2)Filtering 滤流:在数据进行读或写时进行过滤:FilterReader与FilterWriter、FilterInputStream与FilterOutputStream。

    • (3)Converting between Bytes and Characters 转换流:按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader):InputStreamReader、OutputStreamWriter。

    • (4)Object Serialization 对象流 :ObjectInputStream、ObjectOutputStream。

    • (5)DataConversion数据流: 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 。

    • (6)Counting计数流: 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream。

    • (7)Peeking Ahead预读流: 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream。

    • (8)Printing打印流: 包含方便的打印方法 :PrintWriter、PrintStream。
      ##3.2 处理流执行的图示
      这里写图片描述
      ##3.3 缓冲流

    • 【1】对I/O进行缓冲是一种常见的性能优化,缓冲流为I/O流增加了内存缓冲区,增加缓冲区的两个目的:
      (1)允许Java的I/O一次不只操作一个字符,这样提高䇖整个系统的性能;
      (2)由于有缓冲区,使得在流上执行skip、mark和reset方法都成为可能。

    • 【2】缓冲流:它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,
      提高了读写的效率,同时增加了一些新的方法。例如:BufferedReader中的readLine方法,
      BufferedWriter中的newLine方法。

    • 【3】J2SDK提供了4种缓存流,常用的构造方法为:

    //字符输入流
    BufferedReader(Reader in)//创建一个32字节的缓冲区
    BufferedReader(Reader in, int size)//size为自定义缓存区的大小
    
    //字符输出流
    BufferedWriter(Writer out)
    BufferedWriter(Writer out, int size)
    
    //字节输入流
    BufferedInputStream(InputStream in)
    BufferedInputStream(InputStream in, int size)
    
    //字节输出流
    BufferedOutputStream(OutputStream in)
    BufferedOutputStream(OutputStream in, int size)
    
    • 【4】其他
      (1)缓冲输入流BufferedInputSTream除了支持read和skip方法意外,还支持其父类的mark和reset方法;
      (2)BufferedReader提供了一种新的ReadLine方法用于读取一行字符串(以\r或\n分隔);
      (3)BufferedWriter提供了一种新的newLine方法用于写入一个行分隔符;
      (4)对于输出的缓冲流,BufferedWriter和BufferedOutputStream,写出的数据会先在内存中缓存,
      使用flush方法将会使内存的数据立刻写出。

    • 示例1:

    import java.io.*;
    public class TestBufferStream1 {
      public static void main(String[] args) {
        try {
          FileInputStream fis = new FileInputStream(
    		  "d:\\JavaProject\\demo13\\ProcessingStream\\TestBufferStream1.java");
          BufferedInputStream bis = new BufferedInputStream(fis);
          int c = 0;
          System.out.println((char)bis.read());
          System.out.println((char)bis.read());
          bis.mark(100);/*在当前输入流的当前位置上做一个标志,允许最多再读入100个字节*/
          for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
            System.out.print((char)c+" ");
          }
          System.out.println(); 
          bis.reset();/*把输入指针返回到以前所做的标志处*/
          for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
            System.out.print((char)c+" ");
          }
          bis.close();
        } catch (IOException e) {e.printStackTrace();}
      }
    }
    
    • 示例2:
    import java.io.*;
    public class TestBufferStream2
    {
    	public static void main(String[] args)
    	{
    	try{
    	BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\JavaProject\\demo13\\BufferStream\\dat2.txt"));
    	BufferedReader br = new BufferedReader(new FileReader("D:\\JavaProject\\demo13\\BufferStream\\dat2.txt"));
    	String s = null;
    	for(int i=0;i<10;i++)
    	{
    		s = String.valueOf(Math.random());//产生一个小于1的正的随机数,并转换成字符串形式
    		bw.write(s);//把字符串s写入到dat2.txt文件中
    		bw.newLine();//写入一个行分隔符
    	}
    	bw.flush();//使用flush方法将会使内存的数据立刻写出
    
    	while((s=br.readLine()) != null)
    	{
    		System.out.println(s);
    	}
    	bw.close();
    	br.close();
    	}
    	catch(IOException e)
    	{
    		e.printStackTrace();
    	}
    
    	}
    }
    

    ##3.4 转换流

    • 转换流有两种:
      (1)InputStreamReader:将字节流转换为字符流;
      (2)OutputStreamWriter:将字符流转换为字节流。
    • 什么时候使用转换流?由以下分析: 流对象很多,就要明确使用哪个流对象。
    通过三点来完成: 
    	1、明确数据的来源和数据到达的目的地。
    	             来源:输入流 [InputStream,Reader]。 
    	             目的:输出流 [OutputStream,Writer]。 
    	2、操作的数据是否是纯文本。  
    	             是:字符流,使用Reader与Writer; 
    	             否:字节流,使用InputStream与OutputStream。 
    	3、明确要使用哪个具体的对象。 通过设备来进行区分: 
    	             源设备:内存用数组,硬盘就加file,键盘用System.in; 
    	             目的设备:内存用数组,硬盘就加file,键盘用System.out。 
    	4、明确是否还需要其他额外功能:例如 
    	            (1)是否需要较高的效率,即是否需要使用缓冲区,是就加上Buffered;
    	            (2)是否需要转换,是,就使用转换流,InputStreamReader 和 OutputStreamWriter。
    
    • 用一个例子简单的说明: 将键盘录入的数据保存到一个文件中,输入“over”时表示录入结束。 详细分析:
    源:从InputStream,Reader中选择; 因为是键盘录入的是纯文本,所以使用Reader。 
    设备:键盘,所以用System.in; 发现System.in是字节流的操作,与Reader(字符流)矛盾,
    这时就要用到转换流 InputStreamReader 。为了提高操作效率,使用缓冲技术,选择BufferedReader。 
    
    目的:从 OutputStream,Writer中选择。 因为是文本文件,所以选择Writer。 
    设备:硬盘上,一个文件,选择FileWriter。 为了提高操作效率,使用缓冲技术,选择BufferedWriter。 
    
    • 示例1:
    import java.io.*; 
    	class ReadinFile 
    		{ 
    			public static void main(String[] args)throws IOException //这里为了方便阅读,先不做异常处理。 
    			{ 
    				BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in)); 
    				BufferedWriter bufw=new BufferedWriter(new FileWriter("readin.txt")); 
    				String line=null; 
    				while((line=bufr.readLine())!=null) 
    				{ 
    					if("over".equals(line)) break; 
    					bufw.write(line); 
    					bufw.newLine(); 
    				} 
    				bufw.close(); 
    				bufr.close(); 
    			} 
    		}
    
    • 示例2:
    import java.io.*;
    public class TestTransForm 
    {
    	public static void main(String[] args) throws IOException //这里为了方便阅读,先不做异常处理。 
    	{
    		InputStreamReader isr = new InputStreamReader(System.in);
    		BufferedReader br = new BufferedReader(isr);
    		OutputStreamWriter osw = new OutputStreamWriter(
    			                     new FileOutputStream("D:\\JavaProject\\demo13\\TransStream\\TransForm.txt",true));
    		BufferedWriter bw = new BufferedWriter(osw);
    		String str = null;
    		str = br.readLine();
    		while(str != null)
    		{
    			if(str.equalsIgnoreCase("exit")) break;
    			bw.write(str);
    			bw.newLine();
    			str = br.readLine();
    		}
    		br.close();
    		bw.close();
    	}
    }
    
    • 注意:
      (1)构造方法:public FileOutputStream(String name,boolean append) throws FileNotFoundException
      如果append为True,输出字节流就写入文件的末尾,而不是开头(覆盖原来的内容);
      如果append为False,输出字节流就写入文件的开头,即覆盖原来的内容从文件开始处写内容。
      (2)构造方法:public FileOutputStream(String name) throws FileNotFoundException
      每次覆盖原文件的内容,从文件开始处写内容。

    ##3.5 数据流——数据的存储和数据恢复

    • 数据流:DataInputStream和DataOutputStream
      (0)DataInputStream和DataOutputStream是面向字节的,因此要使用InputStream和OutputStream。
      (1)DataInputStream和DataOutputStream分别继承InputStream和OutputStream,
      它们属于处理流,需要分别“套接”在InputStream和OutputStream类型的节点流上。
      (2)DataInputStream和DataOutputStream提供了可以存取与机器无关的Java原始类数据(如:int,double等)的方法。
      (3)DataInputStream和DataOutputStream的构造方法:
         DataInputStream(InputStream in)
    	 DataOutputStream(OutputStream out)
    
    • 示例1:
    import java.io.*;
    public class TestDataStream
    {
    	public static void main(String[] args) throws IOException
    	{
    		FileOutputStream fout = new FileOutputStream("D:/JavaProject/demo13_IO/DataStream/demo.txt",true);
    	    BufferedOutputStream bout = new BufferedOutputStream(fout);
    	    DataOutputStream dout = new DataOutputStream(bout);
    		/*DataOutputStream,BufferedOutputStream,FileOutputStream这里使用了流栈。*/
    
    	    dout.writeInt(110);
    	    dout.writeUTF("hello,中国");
    	    dout.writeFloat(3.14f);
    	    dout.writeChar(97);/*97对应的是'a'*/
            dout.close();/*如果正在使用一个流栈,程序关闭最上面的一个流也就自动的关闭了栈中的所有底层流。*/
    		
    		FileInputStream fin = new FileInputStream("D:/JavaProject/demo13_IO/DataStream/demo.txt");
    		BufferedInputStream bin = new BufferedInputStream(fin);
    		DataInputStream din = new DataInputStream(bin);
    		
    		int i = din.readInt();
    		String str = din.readUTF();
    		float f = din.readFloat();
    		char c = din.readChar();
    		fin.close();/*如果正在使用一个流栈,程序关闭最上面的一个流也就自动的关闭了栈中的所有底层流。*/
            System.out.println("int:"+i+"\nString:"+str+"\nfloat:"+f+"\nchar:"+c);
    	}
    	
    }
    
    • 编译,运行:
    D:\JavaProject\demo13_IO\DataStream>javac TestDataStream.java
    
    D:\JavaProject\demo13_IO\DataStream>java TestDataStream
    int:110
    String:hello,中国
    float:3.14
    char:a
    
    • 注意:
            int i = din.readInt();
    		String str = din.readUTF();
    		float f = din.readFloat();
    		char c = din.readChar();
    		/*此段代码的顺序不能乱,要保证先写入的先读出来的原则,否则会出现错误。
    		*    因此,我们在写代码的时候,我们必须:
    		*         要么为文件中的数据采用固定的格式;
    		*         要么将额外的信息保存到文件中,以便能够对其进行解析以确定数据的寻访位置。
    		*/
    
    展开全文
  • 7、Flink 计算处理和批处理平台

    千次阅读 2018-11-15 12:30:33
    Flink 是一个批处理和流处理结合的统一计算框架,其核心是一个提供了数据分发以及并行化计算的数据处理引擎。它的最大亮点是流处理,是业界最顶级的开源流处理引擎。Flink 与 Storm 类似,属于事件驱动型实时...

    一、Flink 基本概念

    Flink 是一个批处理和流处理结合的统一计算框架,其核心是一个提供了数据分发以及并行化计算的流数据处理引擎。它的最大亮点是流处理,是业界最顶级的开源流处理引擎。Flink 与 Storm 类似,属于事件驱动型实时流系统。

    所谓说事件驱动型指的就是一个应用提交之后,除非明确的指定停止,否则,该业务会一直持续的运行,它的执行条件就是触发了某一个事件,比如在淘宝中,我们付款需要在支付宝付款,但是付款成功与否的条件是从淘宝获取的,支付宝通过接口向淘宝反馈扣款结果,这个计算的应用是一直存在的,它需要获取支付宝扣款的结果,将结果进行计算加入到后台数据库,记录日志并且向淘宝反馈扣款成功的信息。这个时候,这一系列的操作都是由于用户触发了付款这个事件而导致的,之后系统就会进行这个计算,应用是持续存在的,没有事件驱动的情况下,这个应用是处于静止状态的,事件驱动之后,应用进行计算和反馈。

    1.批处理和流处理

    批处理在大数据世界有着悠久的历史。批处理主要操作大容量静态数据集,并在计算过程完成后返回结果。

    批处理模式中使用的数据集通常符合下列特征:

    (1) 有界:批处理数据集代表数据的有限集合

    (2) 持久:数据通常始终存储在某种类型的持久存储位置中

    (3) 大量:批处理操作通常是处理极为海量数据集的唯一方法

    批处理非常适合需要访问全套记录才能完成的计算工作。例如在计算总数和平均数时,必须将数据集作为一个整体加以处理,而不能将其视作多条记录的集合。这些操作要求在计算进行过程中数据维持自己的状态。

    需要处理大量数据的任务通常最适合用批处理操作进行处理。无论直接从持久存储设备处理数据集,或首先将数据集载入内存,批处理系统在设计过程中就充分考虑了数据的量,可提供充足的处理资源。由于批处理在应对大量持久数据方面的表现极为出色,因此经常被用于对历史数据进行分析。大量数据的处理需要付出大量时间,因此批处理不适合对处理时间要求较高的场合。流处理系统会对随时进入系统的数据进行计算。相比批处理模式,这是一种截然不同的处理方式。流处理方式无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作。

    流处理中的数据集是“无边界”的,这就产生了几个重要的影响:

    (1) 完整数据集只能代表截至目前已经进入到系统中的数据总量。

    (2) 工作数据集也许更相关,在特定时间只能代表某个单一数据项。

    (3) 处理工作是基于事件的,除非明确停止否则没有“尽头”。处理结果立刻可用,并会随着新数据的抵达继续更新。

    流处理系统可以处理几乎无限量的数据,但同一时间只能处理一条(真正的流处理)或很少量(微批处理,Micro-batch Processing)数据,不同记录间只维持最少量的状态。虽然大部分系统提供了用于维持某些状态的方法,但流处理主要针对副作用更少,更加功能性的处理(Functional processing)进行优化。

    功能性操作主要侧重于状态或副作用有限的离散步骤。针对同一个数据执行同一个操作会或略其他因素产生相同的结果,此类处理非常适合流处理,因为不同项的状态通常是某些困难、限制,以及某些情况下不需要的结果的结合体。因此虽然某些类型的状态管理通常是可行的,但这些框架通常在不具备状态管理机制时更简单也更高效。

    此类处理非常适合某些类型的工作负载。有近实时处理需求的任务很适合使用流处理模式。分析、服务器或应用程序错误日志,以及其他基于时间的衡量指标是最适合的类型,因为对这些领域的数据变化做出响应对于业务职能来说是极为关键的。流处理很适合用来处理必须对变动或峰值做出响应,并且关注一段时间内变化趋势的数据。

    2.Flink 特点和应用场景

    Flink 最适合的应用场景是低时延的数据处理场景:高并发处理数据,时延毫秒级,且兼具可靠性。

    典型应用场景有:

    (1) 互联网金融业务。

    (2) 点击流日志处理。

    (3) 舆情(舆论情绪)监控。 Flink 的特点有以下几种:

    (1) 低时延:提供 ms 级时延的处理能力。

    (2) Exactly Once:提供异步快照机制,保证所有数据真正只处理一次

    (3) HA:JobManager 支持主备模式,保证无单点故障。

    (4) 水平扩展能力:TaskManager 支持手动水平扩展。

    Flink 能够支持 Yarn,能够从 HDFS 和 HBase 中获取数据;能够使用所有的Hadoop 的格式化输入和输出;能够使用 Hadoop 原有的 Mappers 和 Reducers,并且能与 Flink 的操作混合使用;能够更快的运行 Hadoop 的作业。

    二、Flink 架构

    1.Flink 组件架构

    (1) Data storage 底层是数据存储

    (2) Single node execution 表示的是部署方式

    (3) Local Environment 等表示的是不同的运行环境

    (4) Flink Local Runtime 表示是运行线程

    (5) Flink Optimizer,Flink Stream Builder 等表示的是优化器

    (6) Common API 表示的是 Flink 平台的 API

    (7) Scala API 和 Java API 表示的是对外提供的 API

    该逻辑图按照从上向下的结构,我们可以看出,最高层的组件都是 API 接口,用于提供用户的接入。第二层主要是创建编译工作流,并且对工作流做优化操作。然后将输入的数据按照创建好的工作流去执行,通过 Flink Local Runtime 组件来执行计算。第三层是环境层,不同的数据和应用的执行环境不同,就比如有一些游戏需要运行在 Java 环境中,在对不同的数据进行计算的时候,我们需要的底层环境也是不同的。最下边的一层是部署和数据的最底层,Flink 默认支持两种部署模式,一种是单独部署,也就是指 Flink 直接部署在集群上,作为独立计算工具运行。另一种是 Yarn 部署,就是将 Flink 认知为是 Hadoop 中的一个组件,和 Yarn 对接来使用。这样做可以充分利用各个组件的优势,组件之间互相结合来进行工作的执行。

    2.Flink 的数据结构

    DataStream 是数据模型,所有的数据在进入 Flink 到从 Flink 输出都必须要按照 DataStream 的模型来进行计算和数据的转换。Flink 用类 DataStream 来表示程序中的流式数据。用户可以认为它们是含有重复数据的不可修改的集合(collection),DataStream 中元素的数量是无限的。这里有两个重点,首先, Stream 流式数据可能会存在有重复的数据,这点本身无可厚非,我们在实际写入数据的时候,不同的用户提交相同的数据是很有可能的,而系统也必须要对每一个相同的数据做都做相关的计算操作。那么流数据还有一个特点就是元素是无限的。我们认为流式数据是一个无头无尾的表,旧的数据已经计算完成就被淘汰,而新的数据会被切分成数据分片添加到数据流的结尾。

    DataStream 之间的算子操作:

    (1) 含有 Window 的是窗口操作,与后面的窗口操作相关连,之间的关系可以通过 reduce,fold,sum,max 函数进行管关联。

    (2) connect:进行 Stream 之间的连接,可以通过 flatmap,map 函数进行操作。

    (3) JoinedStream :进行 Stream 之间的 join 操作,类似于数据库中的 join,可以通过 join 函数等进行关联。

    (4) CoGroupedStream:Stream 之间的联合,类似于关系数据库中的 group 操作,可以通过 coGroup 函数进行关联。

    (5) KeyedStream:主要是对数据流依据 key 进行处理,可以通过 keyBy 函数进行处理。

    DataStream 在计算中一共分为了三个步骤:DataSource、Transformation 和DataSink。

    (1) Data source:流数据源的接入,支持 HDFS 文件、kafka、文本数据等。

    (2) Transformations:流数据转换。

    (3) Data sink:数据输出,支持 HDFS、kafka、文本等。

    在 DataStream 中,数据流转换流程与 Spark 类似:

    (1) 从 HDFS 读取数据到 DataStream 中

    (2) 接下来进行相关算子操作,如 flatMap,Map,keyBy

    (3) 接下来是窗口操作或算子操作(4) 最后处理结果 sink 到 HDFS

    三、Flink 执行流程

    (1) Client:Flink  Client 主要给用户提供向 Flink 系统提交用户任务(流式作业)的能力。

    (2) TaskManager :Flink 系统的业务执行节点,执行具体的用户任务。TaskManager 可以有多个,各个 TaskManager 都平等。

    (3) JobManager:Flink 系统的管理节点,管理所有的 TaskManager,并决策用户任务在哪些 Taskmanager 执行。JobManager 在 HA 模式下可以有多个,但只有一个主 JobManager。

    (4) TaskSlot(任务槽):类似 yarn 中的 container 用于资源隔离,但是该组件只包含内存资源,不包含 cpu 资源。每一个 TaskManager 当中包含 3 个 Task Slot,TaskManager 最多能同时并发执行的任务是可以控制的,那就是 3 个,因为不能超过 slot 的数量。 slot 有独占的内存空间,这样在一个 TaskManager 中可以运行多个不同的作业,作业之间不受影响。slot之间可以共享 JVM 资源, 可以共享 Dataset 和数据结构,也可以通过多路复用(Multiplexing) 共享 TCP 连接和心跳消息(Heatbeat Message)。

    (5) Task:任务执行的单元。执行流程:

    (1) 任务的执行流程主要是分成了工作流的下发创建和数据流的执行流程两个部分。在执行数据流计算之前,必须先把任务的执行流程先做好。所以 Client 收到用户提交的应用之后,会通过 FlinkProgram 将用户提交的应用转换成为流式作业,以 Topology 的形式提交到 JobManager 中,该流式作业的 Topology 如果没有在用户的强制指定关闭的情况下,会一直持续的按照事件驱动型进行运行。

    (2) JobManager 通过 Actor 进程和其他组件进行联系,通过 scheduler 进程检查当前集群中所有 TaskManager 中的集群负载,选择负载最小的TaskManager,将任务下发到不同的 TaskManager 中。

    (3) TaskManager 其实可以理解成为是节点,TaskManager 通过 ActorSystem收到 JobManager 的请求之后,下一步会将提交的作业进行下发执行,但是执行之前 TaskManager 还需要检测当前集群资源的使用情况,将内存资源封装成 TaskSlot,下发到其中进行执行。CPU 资源由节点所有进程共享。

    (4) 最终 TaskSlot 执行完任务之后,会将执行的结果直接传送到下一个TaskManager 中,而不是反馈给 JobManager。所以作为 JobManager,其只负责了任务的下发,数据的下发,还有结果的接收,对于所有的中间结果,JobManager 都不负责管理。

     

    (1) Flink YARN Client 首先会检验是否有足够的资源来启动 YARN 集群,如果资源足够的话,会将 jar 包、配置文件等上传到 HDFS。

    (2) Flink YARN Client 首先与 YARN Resource Manager 进行通信,申请启动ApplicationMaster(以下简称 AM)。在 Flink YARN 的集群中,AM 与 Flink JobManager 在同一个 Container 中。

    (3) AM 在启动的过程中会和 YARN 的 RM 进行交互,向 RM 申请需要的 Task ManagerContainer,申请到 Task Manager Container 后,在对应的 NodeManager 节点上启动 TaskManager 进程。

     

    (4) AM 与 Fink JobManager 在同一个 container 中,AM 会将 JobManager 的 RPC 地址通过 HDFS 共享的方式通知各个 TaskManager,TaskManager 启动成功后,会向 JobManager 注册。

    (5) 等所有 TaskManager 都向 JobManager 注册成功后,Flink 基于 YARN 的集群启动成功,Flink YARN Client 就可以提交 Flink Job 到 Flink JobManager,并进行后续的映射、调度和计算处理。

    四、Flink 技术原理

    1.流式数据运行原理

    用户实现的 Flink 程序是由 Stream 数据和 Transformation 算子组成。Stream 是一个中间结果数据,而 Transformation 是算子,它对一个或多个输入 Stream 进行计算处理,输出一个或多个结果 Stream。

     

    Flink 程序执行时,它会被映射为 Streaming Dataflow 。一个 Streaming Dataflow 是由一组 Stream 和 Transformation Operator 组成,它类似于一个DAG 图,在启动的时候从一个或多个 Source Operator 开始,结束于一个或多个Sink Operator。

    (1) Source:流数据源的接入,支持 HDFS 文件、kafka、文本数据等。

    (2) Sink:数据输出,支持 HDFS、kafka、文本等。

    (3) Stream 是 Flink 计算流程中产生的中间数据。Flink 是按 event 驱动的,每个 event 都有一个 event time 就是事件的时间戳,表明事件发生的时间,这个时间戳对 Flink 的处理性能很重要,后面会讲到 Flink 处理乱序数据流时,就是靠时间戳来判断处理的先后顺序。

    一个 Stream 可以被分成多个 Stream 分区(Stream Partitions),一个 Operator 可以被分成多个 Operator Subtask,每一个 Operator Subtask 是在不同的线程中独立执行的。一个 Operator 的并行度,等于 Operator Subtask 的个数,一个Stream 的并行度等于生成它的 Operator 的并行度。

    1.One-to-one 模式比如从 Source[1]到 map()[1],它保持了 Source 的分区特性(Partitioning)和分区内元素处理的有序性,也就是说 map()[1]的 Subtask 看到数据流中记录的顺序,与 Source[1]中看到的记录顺序是一致的。

    2.Redistribution 模式这种模式改变了输入数据流的分区,比如从 map()[1] 、 map()[2] 到 keyBy()/window()/apply()

    [1] 、keyBy()/window()/apply()[2] , 上 游 的Subtask 向下游的多个不同的 Subtask 发送数据,改变了数据流的分区,这与实际应用所选择的 Operator 有关系。 Subtask 的个数,一个 Stream 的并行度总是等于生成它的 Operator 的并行度。

     

    Flink 内部有一个优化的功能,根据上下游算子的紧密程度来进行优化。紧密度高的算子可以进行优化,优化后可以将多个 Operator Subtask 串起想·来组成一个 Operator Chain,实际上就是一个执行链,每个执行链会在 TaskManager 上一个独立的线程中执行。上半部分表示的是将两个紧密度高的算子优化后串成一个 Operator Chain,实际上一个 Operator Chain 就是一个大的 Operator 的概念。途中的 Operator Chain 表示一个 Operator,keyBy 表示一个 Operator,Sink 表示一个 Operator,他们通过 Stream 连接,而每个 Operator 在运行时对应一个 Task,也就是说图中的上半部分 3 个 Operator 对应的是 3 个 Task。下半部分是上半部分的一个并行版本,对每一个 Task 都并行华为多个 Subtask,这里只是演示了 2 个并行度,sink 算子是 1 个并行度。

    2.Flink 窗口技术

    Flink 支持基于时间窗口操作,也支持基于数据的窗口操作:

    (1) 按分割标准划分:timeWindow、countWindow。

    (2) 按窗口行为划分:Tumbling Window、Sliding Window、自定义窗口。窗口按驱动的类型分为时间窗口(timeWindow)和事件窗口(countWindow)。窗口可以是时间驱动的(Time Window,例如:每 30 秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。

    窗口按照其想要实现的功能分为:

     翻滚窗口(Tumbling Window,无时间重叠,固定时间划分或者固定事件个数划分)

    滚动窗口(Sliding Window,有时间重叠)

    会话窗口(Session Window,将事件聚合到会话窗口中,由非活跃的间隙分隔开)。

    3.Flink 容错机制

     

    checkpoint 机制是 Flink 运行过程中容错的重要手段。 checkpoint 机制不断绘制流应用的快照,流应用的状态快照被保存在配置的位置(如:JobManager 的内存里,或者 HDFS 上)。Flink 分布式快照机制的核心是 barriers,这些 barriers 周期性插入到数据流中,并作为数据流的一部分随之流动。barrier 是一个特殊的元组,这些元组被周期性注入到流图中并随数据流在流图中流动。每个 barrier 是当前快照和下一个快照的分界线。在同一条流中 barriers 并不会超越其前面的数据,严格的按照线性流动。一个 barrier 将属于本周期快照的数据与下一个周期快照的数据分隔开来。每个 barrier 均携带所属快照周期的 ID,barrier 并不会阻断数据流,因此十分轻量。Checkpoint 机制是 Flink 可靠性的基石,可以保证 Flink 集群在某个算子因为某些原因(如异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性。该机制可以保证应用在运行过程中出现失败时,应用的所有状态能够从某一个检查点恢复,保证数据仅被处理一次(Exactly Once)。另外,也可以选择至少处理一次(at least once)。

     

     

    每个需要 checkpoint 的应用在启动时,Flink 的 JobManager 为其创建一个CheckpointCoordinator,CheckpointCoordinator 全权负责本应用的快照制作。用 户 通 过 CheckpointConfig 中 的 setCheckpointInterval() 接 口 设 置checkpoint 的周期。

    CheckPoint 机制

    CheckpointCoordinator 周期性的向该流应用的所有 source 算子发送barrier。当某个 source 算子收到一个 barrier 时,便暂停数据处理过程,然后将自己的当前状态制作成快照,并保存到指定的持久化存储中,最后向 CheckpointCoordinator 报告自己快照制作情况,同时向自身所有下游算子广播该 barrier,恢复数据处理。下游算子收到 barrier 之后,会暂停自己的数据处理过程,然后将自身的相关状态制作成快照,并保存到指定的持久化存储中,最后向 CheckpointCoordinator 报告自身快照情况,同时向自身所有下游算子广播该barrier,恢复数据处理。每个算子按照步骤 3 不断制作快照并向下游广播,直到最后 barrier 传递到 sink 算子,快照制作完成。当 CheckpointCoordinator 收到所有算子的报告之后,认为该周期的快照制作成功;否则,如果在规定的时间内没有收到所有算子的报告,则认为本周期快照制作失败。

    展开全文
  • 【Java基础-3】吃透Java IO:字节、字符、缓冲

    万次阅读 多人点赞 2020-09-23 20:12:33
    什么是Java-IO?字符和字节的区别与适用场景是什么?缓冲到底实现了什么?如何高效地读写文件? 本文用大量的示例图和实例,带你吃透Java IO。

    前言

    有人曾问fastjson的作者(阿里技术专家高铁):“你开发fastjson,没得到什么好处,反而挨了骂背了锅,这种事情你为什么要做呢?”

    高铁答道:“因为热爱本身,就是奖励啊!”

    这个回答顿时触动了我。想想自己,又何尝不是如此。写作是个痛苦的过程,用心写作就更加煎熬,需字字斟酌,反复删改才有所成。然而,当一篇篇精良文章出自己手而呈现眼前时,那些痛苦煎熬就都那么值得。如果这些博文能有幸得大家阅读和认可,就更加是莫大的鼓舞了。技术人的快乐就是可以这么纯粹和简单。

    点波关注不迷路,一键三连好运连连!

    IO流是Java中的一个重要构成部分,也是我们经常打交道的。这篇关于Java IO的博文干货满满,堪称全网前三(请轻喷!)

    下面几个问题(问题还会继续补充),如果你能对答如流,那么恭喜你,IO知识掌握得很好,可以立即关闭文章。反之,你可以在后面得文章中寻找答案。

    1. Java IO流有什么特点?
    2. Java IO流分为几种类型?
    3. 字节流和字符流的关系与区别?
    4. 字符流是否使用了缓冲?
    5. 缓冲流的效率一定高吗?为什么?
    6. 缓冲流体现了Java中的哪种设计模式思想?
    7. 为什么要实现序列化?如何实现序列化?
    8. 序列化数据后,再次修改类文件,读取数据会出问题,如何解决呢?

    1 初识Java IO

    IO,即inout,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。

    Java 中是通过流处理IO 的,那么什么是流

    流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。

    当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。

    一般来说关于流的特性有下面几点:

    1. 先进先出:最先写入输出流的数据最先被输入流读取到。
    2. 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
    3. 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

    1.1 IO流分类

    IO流主要的分类方式有以下3种:

    1. 按数据流的方向:输入流、输出流
    2. 按处理数据单位:字节流、字符流
    3. 按功能:节点流、处理流

    在这里插入图片描述

    1、输入流与输出流

    输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反。

    在这里插入图片描述
    2、字节流与字符流

    字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。

    为什么要有字符流?

    Java中字符是采用Unicode标准,Unicode 编码中,一个英文字母或一个中文汉字为两个字节。
    在这里插入图片描述
    而在UTF-8编码中,一个中文字符是3个字节。例如下面图中,“云深不知处”5个中文对应的是15个字节:-28-70-111-26-73-79-28-72-115-25-97-91-27-92-124
    在这里插入图片描述

    那么问题来了,如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。

    字节流和字符流的其他区别:

    1. 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
    2. 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。详见文末效率对比。

    以写文件为例,我们查看字符流的源码,发现确实有利用到缓冲区:
    在这里插入图片描述
    在这里插入图片描述

    3、节点流和处理流

    节点流:直接操作数据读写的流类,比如FileInputStream

    处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)

    处理流和节点流应用了Java的装饰者设计模式。

    下图就很形象地描绘了节点流和处理流,处理流是对节点流的封装,最终的数据处理还是由节点流完成的。
    在这里插入图片描述
    在诸多处理流中,有一个非常重要,那就是缓冲流

    我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
    在这里插入图片描述

    联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。
    在这里插入图片描述
    需要注意的是,缓冲流效率一定高吗?不一定,某些情形下,缓冲流效率反而更低,具体请见IO流效率对比。

    完整的IO分类图如下:
    在这里插入图片描述

    1.2 案例实操

    接下来,我们看看如何使用Java IO。

    文本读写的例子,也就是文章开头所说的,将“松下问童子,言师采药去。只在此山中,云深不知处。”写入本地文本,然后再从文件读取内容并输出到控制台。

    1、FileInputStream、FileOutputStream(字节流)

    字节流的方式效率较低,不建议使用

    public class IOTest {
    	public static void main(String[] args) throws IOException {
    		File file = new File("D:/test.txt");
    
    		write(file);
    		System.out.println(read(file));
    	}
    
    	public static void write(File file) throws IOException {
    		OutputStream os = new FileOutputStream(file, true);
    
    		// 要写入的字符串
    		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
    		// 写入文件
    		os.write(string.getBytes());
    		// 关闭流
    		os.close();
    	}
    
    	public static String read(File file) throws IOException {
    		InputStream in = new FileInputStream(file);
    
    		// 一次性取多少个字节
    		byte[] bytes = new byte[1024];
    		// 用来接收读取的字节数组
    		StringBuilder sb = new StringBuilder();
    		// 读取到的字节数组长度,为-1时表示没有数据
    		int length = 0;
    		// 循环取数据
    		while ((length = in.read(bytes)) != -1) {
    			// 将读取的内容转换成字符串
    			sb.append(new String(bytes, 0, length));
    		}
    		// 关闭流
    		in.close();
    
    		return sb.toString();
    	}
    }
    

    2、BufferedInputStream、BufferedOutputStream(缓冲字节流)

    缓冲字节流是为高效率而设计的,真正的读写操作还是靠FileOutputStreamFileInputStream,所以其构造方法入参是这两个类的对象也就不奇怪了。

    public class IOTest {
    
    	public static void write(File file) throws IOException {
    		// 缓冲字节流,提高了效率
    		BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));
    
    		// 要写入的字符串
    		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
    		// 写入文件
    		bis.write(string.getBytes());
    		// 关闭流
    		bis.close();
    	}
    
    	public static String read(File file) throws IOException {
    		BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
    
    		// 一次性取多少个字节
    		byte[] bytes = new byte[1024];
    		// 用来接收读取的字节数组
    		StringBuilder sb = new StringBuilder();
    		// 读取到的字节数组长度,为-1时表示没有数据
    		int length = 0;
    		// 循环取数据
    		while ((length = fis.read(bytes)) != -1) {
    			// 将读取的内容转换成字符串
    			sb.append(new String(bytes, 0, length));
    		}
    		// 关闭流
    		fis.close();
    
    		return sb.toString();
    	}
    }
    

    3、InputStreamReader、OutputStreamWriter(字符流)

    字符流适用于文本文件的读写OutputStreamWriter类其实也是借助FileOutputStream类实现的,故其构造方法是FileOutputStream的对象

    public class IOTest {
    	
    	public static void write(File file) throws IOException {
    		// OutputStreamWriter可以显示指定字符集,否则使用默认字符集
    		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8");
    
    		// 要写入的字符串
    		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
    		osw.write(string);
    		osw.close();
    	}
    
    	public static String read(File file) throws IOException {
    		InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
    		// 字符数组:一次读取多少个字符
    		char[] chars = new char[1024];
    		// 每次读取的字符数组先append到StringBuilder中
    		StringBuilder sb = new StringBuilder();
    		// 读取到的字符数组长度,为-1时表示没有数据
    		int length;
    		// 循环取数据
    		while ((length = isr.read(chars)) != -1) {
    			// 将读取的内容转换成字符串
    			sb.append(chars, 0, length);
    		}
    		// 关闭流
    		isr.close();
    
    		return sb.toString()
    	}
    }
    

    4、字符流便捷类

    Java提供了FileWriterFileReader简化字符流的读写,new FileWriter等同于new OutputStreamWriter(new FileOutputStream(file, true))

    public class IOTest {
    	
    	public static void write(File file) throws IOException {
    		FileWriter fw = new FileWriter(file, true);
    
    		// 要写入的字符串
    		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
    		fw.write(string);
    		fw.close();
    	}
    
    	public static String read(File file) throws IOException {
    		FileReader fr = new FileReader(file);
    		// 一次性取多少个字节
    		char[] chars = new char[1024];
    		// 用来接收读取的字节数组
    		StringBuilder sb = new StringBuilder();
    		// 读取到的字节数组长度,为-1时表示没有数据
    		int length;
    		// 循环取数据
    		while ((length = fr.read(chars)) != -1) {
    			// 将读取的内容转换成字符串
    			sb.append(chars, 0, length);
    		}
    		// 关闭流
    		fr.close();
    
    		return sb.toString();
    	}
    }
    

    5、BufferedReader、BufferedWriter(字符缓冲流)

    public class IOTest {
    	
    	public static void write(File file) throws IOException {
    		// BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new
    		// FileOutputStream(file, true), "UTF-8"));
    		// FileWriter可以大幅度简化代码
    		BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
    
    		// 要写入的字符串
    		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
    		bw.write(string);
    		bw.close();
    	}
    
    	public static String read(File file) throws IOException {
    		BufferedReader br = new BufferedReader(new FileReader(file));
    		// 用来接收读取的字节数组
    		StringBuilder sb = new StringBuilder();
    
    		// 按行读数据
    		String line;
    		// 循环取数据
    		while ((line = br.readLine()) != null) {
    			// 将读取的内容转换成字符串
    			sb.append(line);
    		}
    		// 关闭流
    		br.close();
    
    		return sb.toString();
    	}
    }
    

    2 IO流对象

    第一节中,我们大致了解了IO,并完成了几个案例,但对IO还缺乏更详细的认知,那么接下来我们就对Java IO细细分解,梳理出完整的知识体系来。

    Java种提供了40多个类,我们只需要详细了解一下其中比较重要的就可以满足日常应用了。

    2.1 File类

    File类是用来操作文件的类,但它不能操作文件中的数据。

    public class File extends Object implements Serializable, Comparable<File>
    

    File类实现了SerializableComparable<File>,说明它是支持序列化和排序的。

    File类的构造方法

    方法名说明
    File(File parent, String child)根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
    File(String pathname)通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
    File(String parent, String child)根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
    File(URI uri)通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。

    File类的常用方法

    方法说明
    createNewFile()当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
    delete()删除此抽象路径名表示的文件或目录。
    exists()测试此抽象路径名表示的文件或目录是否存在。
    getAbsoluteFile()返回此抽象路径名的绝对路径名形式。
    getAbsolutePath()返回此抽象路径名的绝对路径名字符串。
    length()返回由此抽象路径名表示的文件的长度。
    mkdir()创建此抽象路径名指定的目录。

    File类使用实例

    public class FileTest {
    	public static void main(String[] args) throws IOException {
    		File file = new File("C:/Mu/fileTest.txt");
    
    		// 判断文件是否存在
    		if (!file.exists()) {
    			// 不存在则创建
    			file.createNewFile();
    		}
    		System.out.println("文件的绝对路径:" + file.getAbsolutePath());
    		System.out.println("文件的大小:" + file.length());
    
    		// 刪除文件
    		file.delete();
    	}
    }
    

    2.2 字节流

    InputStreamOutputStream是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。

    InputStream为例,它继承了Object,实现了Closeable

    public abstract class InputStream
    extends Object
    implements Closeable
    

    InputStream类有很多的实现子类,下面列举了一些比较常用的:
    在这里插入图片描述
    详细说明一下上图中的类:

    1. InputStreamInputStream是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。
    2. FileInputSream:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。
    3. PipedInputStream:管道字节输入流,能实现多线程间的管道通信。
    4. ByteArrayInputStream:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。
    5. FilterInputStream:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。
    6. DataInputStream:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。
    7. BufferedInputStream:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。
    8. ObjectInputStream:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream的实例对象。

    OutputStream类继承关系图:
    在这里插入图片描述

    OutputStream类继承关系与InputStream类似,需要注意的是PrintStream.

    2.3 字符流

    与字节流类似,字符流也有两个抽象基类,分别是ReaderWriter。其他的字符流实现类都是继承了这两个类。

    Reader为例,它的主要实现子类如下图:
    在这里插入图片描述
    各个类的详细说明:

    1. InputStreamReader:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。
    2. BufferedReader:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader是对InputStreamReader的封装,前者构造器的入参就是后者的一个实例对象。
    3. FileReader:用于读取字符文件的便利类,new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file, true),"UTF-8"),但FileReader不能指定字符编码和默认字节缓冲区大小。
    4. PipedReader :管道字符输入流。实现多线程间的管道通信。
    5. CharArrayReader:从Char数组中读取数据的介质流。
    6. StringReader :从String中读取数据的介质流。

    WriterReader结构类似,方向相反,不再赘述。唯一有区别的是,Writer的子类PrintWriter

    2.4 序列化

    待续…

    3 IO流方法

    3.1 字节流方法

    字节输入流InputStream主要方法:

    • read() :从此输入流中读取一个数据字节。
    • read(byte[] b) :从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
    • read(byte[] b, int off, int len) :从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
    • close():关闭此输入流并释放与该流关联的所有系统资源。

    字节输出流OutputStream主要方法:

    • write(byte[] b) :将 b.length 个字节从指定 byte 数组写入此文件输出流中。
    • write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
    • write(int b) :将指定字节写入此文件输出流。
    • close() :关闭此输入流并释放与该流关联的所有系统资源。

    3.2 字符流方法

    字符输入流Reader主要方法:

    • read():读取单个字符。
    • read(char[] cbuf) :将字符读入数组。
    • read(char[] cbuf, int off, int len) : 将字符读入数组的某一部分。
    • read(CharBuffer target) :试图将字符读入指定的字符缓冲区。
    • flush() :刷新该流的缓冲。
    • close() :关闭此流,但要先刷新它。

    字符输出流Writer主要方法:

    • write(char[] cbuf) :写入字符数组。
    • write(char[] cbuf, int off, int len) :写入字符数组的某一部分。
    • write(int c) :写入单个字符。
    • write(String str) :写入字符串。
    • write(String str, int off, int len) :写入字符串的某一部分。
    • flush() :刷新该流的缓冲。
    • close() :关闭此流,但要先刷新它。

    另外,字符缓冲流还有两个独特的方法:

    • BufferedWriternewLine()写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。
    • BufferedReaderreadLine() :读取一个文本行。

    4 附加内容

    4.1 位、字节、字符

    字节(Byte)是计量单位,表示数据量多少,是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于八位。

    字符(Character)计算机中使用的字母、数字、字和符号,比如’A’、‘B’、’$’、’&'等。

    一般在英文状态下一个字母或字符占用一个字节,一个汉字用两个字节表示。

    字节与字符:

    • ASCII 码中,一个英文字母(不分大小写)为一个字节,一个中文汉字为两个字节。
    • UTF-8 编码中,一个英文字为一个字节,一个中文为三个字节。
    • Unicode 编码中,一个英文为一个字节,一个中文为两个字节。
    • 符号:英文标点为一个字节,中文标点为两个字节。例如:英文句号 . 占1个字节的大小,中文句号 。占2个字节的大小。
    • UTF-16 编码中,一个英文字母字符或一个汉字字符存储都需要 2 个字节(Unicode 扩展区的一些汉字存储需要 4 个字节)。
    • UTF-32 编码中,世界上任何字符的存储都需要 4 个字节。

    4.2 IO流效率对比

    首先,对比下普通字节流和缓冲字节流的效率:

    public class MyTest {
    	public static void main(String[] args) throws IOException {
    		File file = new File("C:/Mu/test.txt");
    		StringBuilder sb = new StringBuilder();
    
    		for (int i = 0; i < 3000000; i++) {
    			sb.append("abcdefghigklmnopqrstuvwsyz");
    		}
    		byte[] bytes = sb.toString().getBytes();
    
    		long start = System.currentTimeMillis();
    		write(file, bytes);
    		long end = System.currentTimeMillis();
    
    		long start2 = System.currentTimeMillis();
    		bufferedWrite(file, bytes);
    		long end2 = System.currentTimeMillis();
    
    		System.out.println("普通字节流耗时:" + (end - start) + " ms");
    		System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms");
    
    	}
    
    	// 普通字节流
    	public static void write(File file, byte[] bytes) throws IOException {
    		OutputStream os = new FileOutputStream(file);
    		os.write(bytes);
    		os.close();
    	}
    
    	// 缓冲字节流
    	public static void bufferedWrite(File file, byte[] bytes) throws IOException {
    		BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(file));
    		bo.write(bytes);
    		bo.close();
    	}
    }
    

    运行结果:

    普通字节流耗时:250 ms
    缓冲字节流耗时:268 ms
    

    这个结果让我大跌眼镜,不是说好缓冲流效率很高么?要知道为什么,只能去源码里找答案了。翻看字节缓冲流的write方法:

    public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }
    

    注释里说得很明白:如果请求长度超过输出缓冲区的大小,刷新输出缓冲区,然后直接写入数据。这样,缓冲流将无害地级联。

    但是,至于为什么这么设计,我没有想明白,有哪位明白的大佬可以留言指点一下。

    基于上面的情形,要想对比普通字节流和缓冲字节流的效率差距,就要避免直接读写较长的字符串,于是,设计了下面这个对比案例:用字节流和缓冲字节流分别复制文件。

    public class MyTest {
    	public static void main(String[] args) throws IOException {
    		File data = new File("C:/Mu/data.zip");
    		File a = new File("C:/Mu/a.zip");
    		File b = new File("C:/Mu/b.zip");
    
    		StringBuilder sb = new StringBuilder();
    
    		long start = System.currentTimeMillis();
    		copy(data, a);
    		long end = System.currentTimeMillis();
    
    		long start2 = System.currentTimeMillis();
    		bufferedCopy(data, b);
    		long end2 = System.currentTimeMillis();
    
    		System.out.println("普通字节流耗时:" + (end - start) + " ms");
    		System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms");
    	}
    
    	// 普通字节流
    	public static void copy(File in, File out) throws IOException {
    		// 封装数据源
    		InputStream is = new FileInputStream(in);
    		// 封装目的地
    		OutputStream os = new FileOutputStream(out);
    		
    		int by = 0;
    		while ((by = is.read()) != -1) {
    			os.write(by);
    		}
    		is.close();
    		os.close();
    	}
    
    	// 缓冲字节流
    	public static void bufferedCopy(File in, File out) throws IOException {
    		// 封装数据源
    		BufferedInputStream bi = new BufferedInputStream(new FileInputStream(in));
    		// 封装目的地
    		BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(out));
    		
    		int by = 0;
    		while ((by = bi.read()) != -1) {
    			bo.write(by);
    		}
    		bo.close();
    		bi.close();
    	}
    }
    

    运行结果:

    普通字节流耗时:184867 ms
    缓冲字节流耗时:752 ms
    

    这次,普通字节流和缓冲字节流的效率差异就很明显了,达到了245倍。

    再看看字符流和缓冲字符流的效率对比:

    public class IOTest {
    	public static void main(String[] args) throws IOException {
    		// 数据准备
    		dataReady();
    
    		File data = new File("C:/Mu/data.txt");
    		File a = new File("C:/Mu/a.txt");
    		File b = new File("C:/Mu/b.txt");
    		File c = new File("C:/Mu/c.txt");
    
    		long start = System.currentTimeMillis();
    		copy(data, a);
    		long end = System.currentTimeMillis();
    
    		long start2 = System.currentTimeMillis();
    		copyChars(data, b);
    		long end2 = System.currentTimeMillis();
    
    		long start3 = System.currentTimeMillis();
    		bufferedCopy(data, c);
    		long end3 = System.currentTimeMillis();
    
    		System.out.println("普通字节流1耗时:" + (end - start) + " ms,文件大小:" + a.length() / 1024 + " kb");
    		System.out.println("普通字节流2耗时:" + (end2 - start2) + " ms,文件大小:" + b.length() / 1024 + " kb");
    		System.out.println("缓冲字节流耗时:" + (end3 - start3) + " ms,文件大小:" + c.length() / 1024 + " kb");
    	}
    
    	// 普通字符流不使用数组
    	public static void copy(File in, File out) throws IOException {
    		Reader reader = new FileReader(in);
    		Writer writer = new FileWriter(out);
    
    		int ch = 0;
    		while ((ch = reader.read()) != -1) {
    			writer.write((char) ch);
    		}
    		reader.close();
    		writer.close();
    	}
    
    	// 普通字符流使用字符流
    	public static void copyChars(File in, File out) throws IOException {
    		Reader reader = new FileReader(in);
    		Writer writer = new FileWriter(out);
    
    		char[] chs = new char[1024];
    		while ((reader.read(chs)) != -1) {
    			writer.write(chs);
    		}
    		reader.close();
    		writer.close();
    	}
    
    	// 缓冲字符流
    	public static void bufferedCopy(File in, File out) throws IOException {
    		BufferedReader br = new BufferedReader(new FileReader(in));
    		BufferedWriter bw = new BufferedWriter(new FileWriter(out));
    
    		String line = null;
    		while ((line = br.readLine()) != null) {
    			bw.write(line);
    			bw.newLine();
    			bw.flush();
    		}
    
    		// 释放资源
    		bw.close();
    		br.close();
    	}
    
    	// 数据准备
    	public static void dataReady() throws IOException {
    		StringBuilder sb = new StringBuilder();
    		for (int i = 0; i < 600000; i++) {
    			sb.append("abcdefghijklmnopqrstuvwxyz");
    		}
    		OutputStream os = new FileOutputStream(new File("C:/Mu/data.txt"));
    		os.write(sb.toString().getBytes());
    
    		os.close();
    		System.out.println("完毕");
    	}
    }
    

    运行结果:

    普通字符流1耗时:1337 ms,文件大小:15234 kb
    普通字符流2耗时:82 ms,文件大小:15235 kb
    缓冲字符流耗时:205 ms,文件大小:15234 kb
    

    测试多次,结果差不多,可见字符缓冲流效率上并没有明显提高,我们更多的是要使用它的readLine()newLine()方法。

    4.3 NIO

    待续…

    展开全文
  • 流处理和批处理框架的异同

    千次阅读 2019-01-23 19:00:00
    A t least once意味着每条消息会进行多次传输尝试,至少一次成功,即消息传输可能重复但不会丢失;Exactly once的消息传输机制是每条消息有且只有一次,即消息传输既不会丢失也不会重复。   容错:流处理框架...
  • bpm的三个标准以及如何结合使用

    万次阅读 2020-06-04 12:47:50
    工作系统、工作引擎、BPM、Camunda BPM
  • 本篇内容 用并行并行处理数据 并行的性能分析 分支/合并框架 使用Spliterator分割
  • 解决HttpServletRequest的只能读取一次的问题写文背景解决思路不能读取多次的原因解决方式借用HttpServletRequestWrapper类来包装最终解决办法总结 写文背景 在使用公司的springboot框架做开发的过程中,参数...
  • C++的标准输入输出

    千次阅读 2018-01-18 21:54:11
     前面曾多次说明,cout和cin并不是C++语言中提供的语句,它们是iostream类的对象,在未学习类和对象时,在不致引起误解的前提下,为叙述方便,把它们称为cout语句和cin语句。正如C++并未提供赋值语句,只提供赋值...
  • Java实现文件写入——IO

    千次阅读 2021-02-12 14:52:34
    输入输出的重要性:输入和输出功能是Java对程序处理数据能力的提高,Java以的形式处理数据。是一组有序的数据序列,根据操作的类型,分为输入和输出。程序从输入读取数据,向输出写入数据。Java是面向...
  • 以新的数据仓库平台为基础,结合行内的通用文件传输平台、统一调度平台,规范了源数据系统的数据报送,梳理构建了新的数据模型,大数据平台解决了传统数仓在批量数据处理能力的不足,在相关任务上体验到了从数小时到...
  • 媒体及媒体传输协议简介

    千次阅读 多人点赞 2019-06-01 22:26:10
    媒体(streaming media):是指将一连串的媒体数据压缩后,经过网上分段发送数据,在网上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个...
  • 媒体技术介绍

    千次阅读 多人点赞 2021-03-08 17:11:36
    本文简单介绍媒体技术的相关知识。 1 概述 媒体(streaming media)技术,是指将一连串的多媒体数据压缩后,经过互联网分段发送数据,在互联网上...媒体包括声音、视频、文本、图像、动画等,在时
  • istream:输入类型,提供输入操作。 ostream:输出类型,提供输出操作。 cin:istream对象,从标准输入读取数据。 cout:ostream对象,向标准输出写入数据。 cerr:ostream对象,向标准错误写入数据。 >>...
  • C++文件输入/输出

    千次阅读 2022-03-24 19:16:48
    文章目录文件输入/输出类表5: 文件读写模式表6:ios_base 标识的组合意义ofstream:写入文件的类例8:输出文本文件例9:输出二进制文件ifstream:从文件中读取的类例10:输入文本文件例11:输入二进制文件表7:...
  • Streaming 101批处理之外的流处理世界

    千次阅读 2017-09-13 09:12:00
    Streaming 101批处理之外的流处理世界 本文整理谷歌Tyler Akidau写的两篇文章,对于技术人员来理解大数据计算中的一些概念非常有用。原文写于2015年,所以对有些问题的是不准确的,但是不影响文章所表达的主要宗旨...
  • 检查日志文件、读取配置 文件、处理数据元素,shell脚本可以帮助我们将文本文件中各种数据的日常处理任务自动化。但仅靠shell脚本命令来处理文本文件的内容有点力不从心的。如果想在shell脚本中处理任何类型的数据,...
  • 中文自然语言处理入门实战

    万次阅读 多人点赞 2018-07-03 02:45:10
    NLP 作为 AI 技术领域中重要的分支,随着其技术应用范围不断扩大,在数据处理领域占有越来越重要的地位。本达人课,作为中文自然语言处理边学边实战的入门级教程,以小数据量的“简易版”实例,通过实战带大家快速...
  • 在MPEG-2中,为适应隔行扫描视频信号的特点,在DCT、预测和运动估计算法中对帧和场进行了不同的处理。 另一方面,MPEG-2根据不同的编码工具定义了5个Profile:简单SP、主要MP、SNR可分级SNP、空间可分级SSP和高级...
  • 本文介绍了Java IO的基本概念,使用方法,以及使用的注意事项等。帮助你更好地理解和使用Java的IO。 具体代码在我的GitHub中可以找到 ... 喜欢的话麻烦点一下星哈谢谢。...更关于Java后...
  • 媒体网络传输协议 1RTP 2RTCP 3RTSP 4RSVP 三媒体播放方式 1单播 2组播 3点播与广播 四业界中媒体系统的简介 一、音视频编解码技术1、MPEG4 MPEG全称是Moving Pictures Experts Group,它是“动态图象专家...
  • 28181协议全称为GB/T28181《安全防范视频监控联网系统信息传输、交换、控制技术要求》,是由公安部科技信息化局提出,由全国安全防范报警系统标准化技术委员会(SAC/TC100)归口,公安部一所等家单位共同起草的一部...
  • 实时协议(RTSP)简介

    千次阅读 2019-05-25 14:45:36
    RTSP(Real Time Streaming Protocol),RFC2326,实时流传输协议,是...协议定义了一对应用程序如何有效地通过IP网络传送多媒体数据。RTSP在体系结构上位于RTP和RTCP之上,它使用TCP或UDP完成数据传输。HTTP与R...
  • 网络媒体(四)———TS

    万次阅读 多人点赞 2019-08-04 11:54:59
    1. 数字视频压缩MPEG-2标准 MPEG-2是MPEG(Moving Picture Experts Group,...与MPEG-1标准相比,MPEG-2标准具有更高的图像质量、更的图像格式和传输码率的图像压缩标准。MPEG-2标准不是MPEG-1的简单升级,而是...
  • C++的输入和输出与标准输出

    万次阅读 2016-09-20 11:23:35
     前面曾多次说明,cout和cin并不是C++语言中提供的语句,它们是iostream类的对象,在未学习类和对象时,在不致引起误解的前提下,为叙述方便,把它们称为cout语句和cin语句。正如C++并未提供赋值语句,只提供赋值...
  • 今天帮人做了个简单的作业,没想到花时间最多的不是算法而是文件的读写,还有对读入字符串的分割处理。晚上写作业的时候又用到了对字符串的处理,这里记录一下。 代码有查阅各种资料,但是成品为原创。 重点是文件...
  • 史上最骚最全最详细的IO教程,小白都能看懂!

    万次阅读 多人点赞 2019-10-31 08:54:47
    相信大家都能体会到,io用到的地方很,就比如上传下载,传输,设计模式等....基础打扎实了,才能玩更高端的。 在博主认为真正懂IO的优秀程序员每次在使用IO之前都会明确分析如下四点: > (1)明确要...
  • 标准IO

    千次阅读 2018-03-09 13:54:51
    一、文件I/O和标准I/O 二、标准I/O 2.1 错误报告 2.2 和FILE结构 2.2.1 简述 2.2.2 打开 2.2.3 进程启动自动打开 2.2.4 FILE结构 ...2.3.5 进程与缓冲 2.4 标准I/O函数 2.4.1 简述 2.4...
  • 为响应式(Reactive Streams)增加的发布-订阅(publisher-subscriber)框架、并发包CompletableFuture类的增强,等等。。 JEP266中为Java语言的并发性又引入许多新的方式:响应式,一个为它而生互操作性更强的发布-...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 449,992
精华内容 179,996
关键字:

多次流标该如何处理