NIO/BIO
NIO/BIO
BIO网络通信
概述
网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。
在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。
该模型最大的问题就是缺乏弹性伸缩能力,
当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接。在高性能服务器应用领域,往往需要面向成千上万个客户端的并发连接,这种模型显然无法满足高性能、高并发接入的场景。
引入线程池
为了解决同步阻塞I/O面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化——后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N。
通过线程池可以灵活地调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。
由于线程池和消息队列都是有界的,而且避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题
因此,无论客户端并发连接数多大,它都不会导致线程个数过于膨胀或者内存溢出。相比于传统的一连接一线程模型,是一种改良。
BIO通信框架的弊病
引入线程池避免了创建大量线程,但是由于底层的通信依然采用同步阻塞模型,其实并未从根本上解决问题
当对Socket的输入流进行读取操作的时候,它会一直阻塞下去,直到发生如下三种事件。
1)有数据可读;
2)可用数据已经读取完毕;
3)发生空指针或者I/O异常。
这意味着当对方发送请求或者应答消息比较缓慢,或者网络传输较慢时,读取输入流一方的通信线程将被长时间阻塞,如果对方要60s才能够将数据发送完成,读取一方的I/O线程也将会被同步阻塞60s,在此期间,其他接入消息只能在消息队列中排队。
BIO的网络通信,读和写操作都是同步阻塞的,阻塞的时间取决于对方I/O线程的处理速度和网络I/O的传输速度。
本质上来讲,我们无法保证生产环境的网络状况和对端的应用程序能足够快,如果我们的应用程序依赖对方的处理速度,它的可靠性就非常差
无论是否引入线程池技术,如果底层用的是BIO通信框架,都是无法从根本上解决通信线程阻塞问题。
BIO 4种产生阻塞的方法:
1-4
//accept方法会产生阻塞,直到有客户端连接
//传统的BIO会产生阻塞:
//1.服务端accept()方法会产生阻塞
//当有客户端接入时,accpet()方法不阻塞
//但是客户端没有任何的流输入,所以产生了阻塞
//2.即read()方法也会产生阻塞
//3.如果服务端未启动,客户端就连接的话会报错,Connection refused
//但是,需要留意的是,这个异常在程序启动后,并不是马上抛出的
//而是卡顿了一秒钟才出现的
//这个现象的原因:
//客户端程序启动=》尝试连接服务端=》等待服务端的链接响应=》服务端没有启动=》
//客户端收到服务端的响应,报出错误提示
//实际上,对应客户端,socket.connect()这个方法也会产生阻塞
4.//结果证明,当不断向outputStream里写数据时,写到一定大小后,会产生阻塞
//即Write方法也会产生阻塞
NIO
NIO概述
Non-Blocking I/O,是一种非阻塞通信模型。不同的语言或操作系统都有其不同的实现。
java.nio是jdk1.4版本引入的一套API,我们可以利用这套API实现非阻塞的网络编程模型。
目前无论是何种应用,都是分布式架构,因为分布式架构能够抗高并发,实现高可用,负载均衡以及存储和处理海量数据,而分布式架构的基础是网络通信。
随着当前大数据和实时计算技术的兴起,高性能 RPC 框架与网络编程技术再次成为焦点。
Fackebook的Thrift框架
scala的 Akka框架
实时流领域的 Storm、Spark框架
开源分布式数据库中的 Mycat、VoltDB
Java 领域里大名鼎鼎的 NIO 框架——Netty,则被众多的开源项目或商业软件所采用。
BIO和NIO的对比
BIO
1.阻塞通信模型,典型代表是ServerSocket 和Socketaccept connect read write 会产生阻塞。所以BIO通信模型的弊端在于:如果有大量请求,会创建大量线程,一是可能造成内存溢出,此外,线程多了之后,会造成cpu的负载过高,因为要做线程管理和上下文切换。虽然引入线程池,也未能解决根本问题,因为底层还是同步阻塞模型。同步阻塞模型一是性能低,二是不可靠,取决于对端环境。
2.面向流处理,即阻塞最根本的原子在于流的read和wirte方法是阻塞方法
Java NIO 管道是2个线程之间的单向数据连接。
Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取
BIO的使用场景:访问量少,长请求(比如下载一个大文件等场景)
NIO的适用场景:高并发,高访问量,短请求
NIO
1.非阻塞通信模型
2.面向缓冲区(Buffer)
3.NIO传输数据的方式:把数据放到缓冲区,然后通过Channel进行传输。
Buffer
Buffer的子类,对应了8种基本数据类型里的七种(没有boolean类型),分别是:
1.ByteBuffer
2.CharBuffer
3.DoubleBuffer
4.FolatBuffer
5.IntBuffer
6.LongBuffer
7.ShortBuffer
1.创建缓冲区
static ByteBuffer allocate(int capacity)参数:capacity 缓冲区的容量,以字节为单位
2.向缓冲区里写数据
put(byte b)
put(byte[] src)
putXxx()
3.从缓冲区里读数据
get()
当向缓冲区里写入字节时,比如1,当调用get()方法时,得到的却是0
4.Buffter缓冲区的4个关键元素
①capacity,缓存区总容量
②limit的大小<=capacity的大小,创建缓冲区时,默认=capacity
③position,初始位置在缓存区的0位,当写入数据时,数据的写入位置就是position的位置
写完后,position的位置+1。
④mark,标记
5.filp()方法:
flip()反转缓冲区,作用相当于buffer.limit(buffer.position());+ buffer.position(0);
HasRemaining方法:
告知当前位置(position)和限制位(limit)之间是否还有元素
6.rewind()重绕缓冲区
将position置为0
7.clear()清空缓冲区
//clear的作用是清空缓冲区,但并不真正的清除数据,而是把postion置为0,limit置为容量上限
//所以,当get(0)时,是可以得到原缓冲区数据的。但是我们一般都是在clear()方法之后,写数据,然后filp()
//所以,并不影响缓冲区的重用。
Channel
Channel:通道,面向缓冲区,进行双向传输
总接口:Channel
其中重点关注他的4个子类
操作TCP的SocketChannel
SocketChannel默认也是非阻塞的,需要个更改下configureBlocking(false);
ServerSocketChannel是一个抽象类,不能直接new,所以调用其静态方法open()
创建出来之后,默认是阻塞的,如果要设置成非阻塞模式,需要设置:
ssc.configureBlocking(false); 属性为false表示非阻塞
和ServerSocketChannel
SocketChannel默认也是非阻塞的,需要个更改下configureBlocking(false);
操作UDP的DatagramChannel
操作文件的FileChannel
* 这个方法用来测试FileChannel,FileChannel只能通过FileInputStream,FileOutputStream和
* RandomAccessFile的getChannel()方法得到。
* FileChannel在文件操作上,性能上没什么差别。读或写都是通过缓冲区来操作。此外还提供了一些额外方法,比如可以指定从文件的某个位置开始读或写
* 如果FileChannel是通过FileInputStream得到,那他只能读文件,不能写文件。
* 通过RandomAccessFile得到的FileChannel,既可以对指定文件读也可以写。并且都可以指定开始读或写的位置
Selector
Selector 多路复用器:
可以理解为路由器和交换机
在一个Selector上可以同时注册多个非阻塞的通道,从而只需要很少的线程数既可以管理许多通道。特别适用于开启许多通道但是每个通道中数据流量都很低的情况
//select()是选择器selector查询是否有事件触发的方法,比如accpet事件是否触发,如果accpet事件触发:
//就意味着有客户端接入了。注意,select()是一个阻塞方法。当有事件被触发时,阻塞放开。
//引入selector的好处是:线程不必每时每刻都去工作、去查询客户端是否有新事件,没有事件的时候,线程就睡觉,休息
//有事件发生,selector会知道,线程再醒来工作。这样一来,可以避免了线程无意义的空转,节省cpu资源,同时也不影响工作
Zero-Copy
原理概述
zero copy(零复制)是一种特殊形式的内存映射,它允许你将Kernel内存直接映射到设备内存空间上。其实就是设备可以通过直接内存访问(direct memory access,DMA)方式来访问Kernal Space。
我们拿Kafka举例,Kafka会对流数据做持久化存储以实现容错
这意味着当Consumer从Kafka消费数据时,会有很多data从硬盘读出之后,会原封不动的通过socket传输给用户。
数据都被拷贝了四次!
Zero Copy的具体实现
实际上第二次和第三次copy是毫无意义的。应用程序仅仅缓存了一下data就原封不动的把它发回给socket buffer。实际上,data应该直接在read buffer和socket buffer之间传输,
为什么要使用kernel buffer做中介
使用kernel buffer做中介(而不是直接把data传到user buffer中)看起来比较低效(多了一次copy)。
然而实际上kernel buffer是用来提高性能的。
在进行读操作的时候,kernel buffer起到了预读cache的作用。当写请求的data size比kernel buffer的size小的时候,这能够显著的提升性能
在进行写操作时,kernel buffer的存在可以使得写请求完全异步。
但悲剧的是,当读请求的data size远大于kernel buffer size的时候,这个方法本身变成了性能的瓶颈。
NIO/BIO的更多相关文章
- java aio nio bio
转自:http://blog.csdn.NET/liuxiao723846/article/details/45066095 Java中的IO主要源自于网络和本地文件 IO的方式通常分为几种,同步阻塞 ...
- 关于同步,异步,阻塞,非阻塞,IOCP/epoll,select/poll,AIO ,NIO ,BIO的总结
相关资料 IO基本概念 Linux环境 同步异步阻塞非阻塞 同步与异步 阻塞与非阻塞 IO模型Reference Link 阻塞IO模型 非阻塞IO模型 IO复用模型 信号驱动异步IO模型 异步IO模 ...
- Nio Bio Netty Tomcat的NIO
socket():新建一个文件 bind():绑定到端口,第一个参数就是socket()方法产生的文件描述符 listen():确定新建的这个socket是一个服务器,被动等待网络其他进程链接,参数有 ...
- Java之NIO,BIO,AIO
Hollis知识星球的一些学习笔记,有兴趣的朋友可以微信搜一下 什么是NIO 什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 ...
- 【转载】Java NIO学习 & NIO BIO AIO 比较
可以参考这个页面: http://www.iteye.com/magazines/132-Java-NIO (下面这个页面也有) http://ifeve.com/overview/ 另,在这篇文章里 ...
- java IO NIO BIO 最权威的总结
1. BIO (Blocking I/O) 1.1 传统 BIO 1.2 伪异步 IO 1.3 代码示例 1.4 总结 2. NIO (New I/O) 2.1 NIO 简介 2.2 NIO的特性/N ...
- Java后端进阶-网络编程(NIO/BIO)
Socket编程 BIO网络编程 BIO Server package com.study.hc.net.bio; import java.io.BufferedReader; import java ...
- Apache Tomcat 7 Configuration BIO NIO AIO APR ThreadPool
Apache Tomcat 7 Configuration Reference (7.0.93) - The Executor (thread pool)https://tomcat.apache.o ...
- BIO & NIO & NIO常见框架
BIO & NIO BIO - Blocking IO - 同步式阻塞式IO --- UDP/TCP NIO - New IO - 同步式非阻塞式IO AIO - Asynchronous ...
随机推荐
- 20165213 Exp5 MSF基础应用
Exp5 MSF基础应用 实践内容 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路.具体需要完成: 1.1一个主动攻击实践,如ms08_067; (1分) 1.2 一 ...
- python3 第二十七章 - 内置函数之str相关
Python 的字符串常用内建函数如下: 序号 方法及描述 实例 1 capitalize()将字符串的第一个字符转换为大写 2 center(width, fillchar) 返回一个指定的宽度 ...
- 移动APP测试入手点
- linux中tomcat startup.sh出现commond not found
问题: 前些天,再Linux提交更新代码启动tomcat时报commond not found 过程: 查了下百度,http://code2care.org/2015/-bash:-startup.s ...
- remove集合的用法
循环集合的方法有三种: 简单for循环 iterator循环 增强for循环 例子如下: List<Long> fList = new ArrayList<Long>(); f ...
- CentOS 7.6下解决登录MySQL时,ERROR 1045 (28000): Access denied for user root@localhost (using password: YES
https://blog.csdn.net/sinat_35406909/article/details/79763782
- 预装win8的笔记本用第三方分区软件分区后出现0x0000225错误的解决方法/同理win7
最近为采用EFI分区的联想电脑分区,是通过第三方软件进行的,完成后重启,发现系统报错0x0000225,提示插入安装介质修复. 应该是EFI分区导致的 http://zhidao.baidu.com/ ...
- Python 设计模式之路
备注:本套笔记内容来源于互联网,只做学习使用,如有侵权请联系本笔记作者. 资料内容 Python 设计模式之路(一)——设计模式 初识 Python 设计模式之路(二)——简单工厂.工厂.抽象工厂模式 ...
- (三)Javascript面向对象编程:非构造函数的继承
Javascript面向对象编程:非构造函数的继承 这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现"继承". 今天是最后一个部分,介绍不使 ...
- App性能测试之启动时间(安卓)手动+脚本
这个测试可以使用adb工具,adb的安装方式 测试策略 安装后首次启动 常规冷启动 热启动(一般这个都很少测试) 针对1和2的测试方法 步骤1:在cmd中输入如下命令 adb logcat * > ...