转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10857412.html

一:IO流梳理——字符流、字节流、输入流、输出流

  见另一篇博文:https://www.cnblogs.com/ygj0930/p/5827509.html

  

二:同步&异步、阻塞&非阻塞的概念

  同步:同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。

  异步:当一个异步过程调用发出后,调用者不能立刻得到结果,可以先做其他事。当这个调用在完成后,通过状态、通知和回调来通知调用者。

  同步与异步 是 调用与结果 的关系。

  阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(挂起:线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。

     同步 不等于 阻塞:对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还是会抢占cpu去执行其他逻辑,也会主动检测io是否准备好

  非阻塞:调用函数时,在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,在之后通过select通知调用者。

  阻塞与非阻塞 是 调用与线程状态 的关系。

三:Linux的5 种 IO 模型

  1)阻塞I/O(blocking I/O)

  应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….直到数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

  2)非阻塞I/O (nonblocking I/O)

  非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回),而在IO过程中,进程是阻塞的。

  我们把一个SOCKET接口设置为非阻塞的意思就是:告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。

  3) I/O复用(select 和poll) (I/O multiplexing)  

  能实现同时对多个IO端口进行监听; I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

  4)信号驱动I/O (signal driven I/O (SIGIO))
  首先我们允许进程进行信号驱动I/O,并定义一个信号处理函数;

  当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

  以上四种其实都是同步IO。

  5)异步I/O (asynchronous I/O (the POSIX aio_functions))

  当一个异步过程调用发出后,调用者不能立刻得到结果,它转身去做其他事情;

  被调用者在执行完毕后,通过状态、通知、回调等信息,通知调用者做完了,然后调用者再接着之前的工作往下进行。

三:BIO、NIO 和 AIO 的区别

 BIO:同步并阻塞IO,一个连接一个线程。

 

 NIO:轮询,通道有请求就调用对应处理函数。

 “多路复用IO+同步非阻塞IO”,一个单线程Selector阻塞轮询,找到有数据的channel进行非阻塞IO。

 NIO是基于事件驱动模式的IO,Reactor模式是事件驱动模式的实现,主要原理见:https://www.jianshu.com/p/eef7ebe28673

 Selector可以轮询多个Channel,因为JDK使用了epoll()代替传统的select实现,没有最大连接句柄限制,所以只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。

 应用程序向selector注册一个channel,由selector不断轮询,如果发现有某个channel发生连接请求,就会通知相应的处理线程。

 

 AIO:订阅,操作系统有IO就通知程序处理。

 AIO框架在windows下使用windows IOCP技术,在Linux下使用epoll多路复用IO技术模拟异步IO。

 应用程序向操作系统注册IO监听,然后继续做自己的事情。操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数。

四:Netty框架

  1、Java原生NIO存在的问题

  1)NIO 的类库和 API 繁杂,使用麻烦:你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。

  2)需要具备其他的额外技能做铺垫:例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。

  3)可靠性能力补齐,开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。

  4)JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。

  2、Netty适用场景

  Netty 对 JDK 自带的 NIO 的 API 进行了封装,主要使用场景如下:

  1)互联网行业:在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 RPC 框架使用。

    典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。

  2)大数据领域:经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现。

  3、Netty的高性能设计

  Netty 是异步事件驱动的,高性能之处主要来自于其 I/O 模型和线程处理模型,前者决定如何收发数据,后者决定如何处理数据

  1)基于 I/O多路复用模式

  Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型:

  

  2)面向Buffer的数据读写

  传统的 I/O 是面向字节流或字符流的,以流式的方式顺序地从一个 Stream 中读取一个或多个字节, 因此也就不能随意改变读取指针的位置。

  在 NIO 中,抛弃了传统的 I/O 流,而是引入了 Channel 和 Buffer 的概念L:在 NIO 中,只能从 Channel 中读取数据到 Buffer 中或将数据从 Buffer 中写入到 Channel,可以随意地读取任意位置的数据。

  3)事件驱动的线程模型

  发生事件时,主线程把事件放入事件队列。

  在另外的线程中不断循环消费事件列表中的事件,调用事件对应的处理逻辑处理事件。

  事件驱动方式也被称为消息通知方式,其实是设计模式中观察者模式的实现

  包括 4 个基本组件:

  1)事件队列(event queue):接收事件的入口,存储待处理事件;

  2)分发器(event mediator):将不同的事件分发到不同通道;

  3)事件通道(event channel):分发器与业务处理器之间的联系渠道;

  4)事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作。

  此模式的优点:

  1)可扩展性好:基于分布式的异步架构,事件与处理器之间高度解耦,可以方便扩展事件处理逻辑;

  2)高性能:基于队列暂存事件,能方便并行异步处理事件。

  NIO的线程模型——Reactor模型(反应堆模型)

  服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程.

  Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。

  Reactor 模型中有 2 个关键组成:

  1)Reactor:Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应。

  2)Handlers:处理程序执行 I/O 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。

  Netty的线程模型——基于NIO的主从 Reactors 多线程模型作进一步修改

  a)MainReactor 负责客户端的连接请求,并将请求转交给 SubReactor;

  b)SubReactor 负责相应通道的 IO 读写请求;

  c)非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理。

  4)基于异步的事件处理方式

  Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个 ChannelFuture。

  调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果

  当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作。

  

  

  

  

Java IO全面的更多相关文章

  1. java.IO输入输出流:过滤流:buffer流和data流

    java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...

  2. Java:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...

  3. Java IO之字符流和文件

    前面的博文介绍了字节流,那字符流又是什么流?从字面意思上看,字节流是面向字节的流,字符流是针对unicode编码的字符流,字符的单位一般比字节大,字节可以处理任何数据类型,通常在处理文本文件内容时,字 ...

  4. java Io流向指定文件输入内容

    package com.hp.io; import java.io.*; public class BufferedWriterTest{ public static void main(String ...

  5. java Io文件输入输出流 复制文件

    package com.hp.io; import java.io.FileInputStream; import java.io.FileNotFoundException; import java ...

  6. java Io流更新文件内容

    package com.hp.io; import java.io.FileOutputStream; import java.io.IOException; public class FileOut ...

  7. java IO流详解

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  8. java.io.NotSerializableException: test.io.file.Student

    java.io.NotSerializableException: test.io.file.Student    at java.io.ObjectOutputStream.writeObject0 ...

  9. java.io.IOException: mark/reset not supported

    java.io.IOException: mark/reset not supported at java.io.InputStream.reset(InputStream.java:348) at ...

  10. Java IO流学习总结

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

随机推荐

  1. zz【重磅】微软开源自动机器学习工具 - NNI

    [重磅]微软开源自动机器学习工具 - NNI 在机器学习建模时,除了准备数据,最耗时耗力的就是尝试各种超参组合,找到模型最佳效果的过程了.即使是对于有经验的算法工程师和数据科学家,有时候也很难把握其中 ...

  2. VC修改本机IP地址

    http://www.vcchar.com/thread-1527-1-1.html 设置IP地址只需要更改注册表中关于适配器的相应设置,但更改后需要重新启动系统才能生效,而AddIPAddress函 ...

  3. Linux性能优化实战学习笔记:第二十五讲

    一.磁盘性能指标 1.使用率 2.饱和度 3.IOPS 4.吞吐量 5.响应时间 6.性能测试工具 二.磁盘I/O观测 1.每块磁盘的使用率(指标实际上来自/proc/diskstats) [root ...

  4. [LeetCode] 116. Populating Next Right Pointers in Each Node 每个节点的右向指针

    You are given a perfect binary tree where all leaves are on the same level, and every parent has two ...

  5. Metasploit 常用命令手册

    Installation curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/template ...

  6. .NetCore 使用k8s部署服务的过程中需要注意的地方以及遇到的问题

    这里开始我准备了3台测试服务器,这里我使用了JumpServer管理起来了,这里我们来看下: Master :192.168.0.236 Node1:192.168.0.237 Node2:192.1 ...

  7. Navicat的安装和pymysql模块的使用

    内容回顾 select distinct 字段1,字段2,... from 表名 where 分组之前的过滤条件 group by 分组条件 having 分组之后过滤条件 order by 排序字段 ...

  8. js 为url字符串添加、修改参数

    //为url字符串添加.修改参数 String.prototype.EditUrlParam = function (paramName, replaceWith) { var url = this; ...

  9. aicoder实习svn内部培训教程

    1 svn介绍 都已经用了git,为什么还要介绍svn呢?为了让aicoder.com的学员能够掌握多种的源代码管理工具的使用方法,更快的融入到项目开发中,特整理此文档. 1.1 项目管理中的版本控制 ...

  10. Java基础之UDP协议和TCP协议简介及简单案例的实现

    写在前面的废话:马上要找工作了,做了一年的.net ,到要找工作了发现没几个大公司招聘.net工程师,真是坑爹呀.哎,java就java吧,咱从头开始学呗,啥也不说了,玩命撸吧,我真可怜啊. 摘要: ...