Java NIO浅读
date
Jun 30, 2020
slug
Java_NIO浅读
status
Published
tags
Java
summary
type
Post
花了半天时间啃完一篇Java NIO教程,整理一下内容
BIO vs NIOBIO:NIO:Channel,Buffer之间的数据传输方式:IO模型IO复用、异步IO区别NIO ServerNIO Server需要自己处理TCP拆包粘包问题什么时候用IO,什么时候用NIO?
BIO vs NIO
BIO:
单向读取/写入,InputStream,OutputStream,当拿到InputStream/OutputStream对象时,系统已读完内容到内存。
NIO:
双向读取读取/写入,使用Buffer,Channel。Channel可以理解为连接,充Channel读取内容到Buffer后,如需要从Buffer写内容到Channel,需要将buffer翻转,调用buffer.flip()方法。读取需要字节读取,buffer可以设置读取的字节容量大小。
Channel,Buffer之间的数据传输方式:
- Buffer从一个或多个Channel中读取内容
- Buffer往一个或多个Channel中写入内容
- Channel到Channel的数据传输
IO模型
IO分两步,等待可读/写,真正读/写,常用的五种IO模型:
IO复用、异步IO区别
非阻塞IO(NIO)有IO复用、信号驱动IO(AIO)两种实现方式,它们真正读写的过程是阻塞的,而异步IO整个IO过程都不阻塞的。NIO关心的是“可以读写了”,AIO关心的是“读写完了”。
NIO Server
NIO Server用的是IO多路复用模型,所有的Channel连接时向Selector注册感兴趣的事件,selector另起一个线程循环检查可读写的Channel,当读写就绪时调用对应的Handler处理,由于读写是memory copy,性能很高,NIO Server只需少量的线程便可以处理大量的连接。
NIO Server需要自己处理TCP拆包粘包问题
由于从Channel中读取信息是按字节读取,不能保证每次读取都能包含一段完整数据报,对于不完整的数据报内容需要先缓存起来,等待下一次读取的时候组成数据报。
一个MessageReader对应一个Channel,MessageReader负责吧Data Block转换成Message,为了缓存不完整的Message,如果一个MessageReader一个缓存空间,假如最大的Message有1MB,那么1,000,000个连接就需要1,000,0000x1MB=1TB内存,那如果最大的消息有16MB呢?这显然是不合理的。使用可调整大小缓存空间可以解决这个问题:
- Resize by Copy
先分配一块小的缓存空间,当消息到达后发现空间不够用就再开辟一块新的大的缓存空间,把原来的缓存空间内容复制过来,以此类推。这样的好处是缓存的内容在内存地址上是连续的,消息的解析更为方便,坏处是可能会产生大量的Copy操作。
- Resize By Append
先分配一块小的缓存空间,不够用时在加分配一块缓存空间,两个缓存空间内存地址上不连续,类似链表。这样的好处是可以避免大量的Copy操作,坏处是后面的消息解释处理比较复杂。
- TLV
TLV即是Type, Length, Value,消息TLV值放在消息头部,这样当消息头部到达时MessageReader就知道需要分配多少缓存空间大小了,这样效率更高,内存管理更优秀。由于在消息完全到达前就已经分配好空间大小,如果一个连接的消息体太大可能会消耗服务器内存导致无响应。
什么时候用IO,什么时候用NIO?
面对少量连接的时候使用IO更方便而不会造成太大的性能损失,面对大量连接使用NIO,这样同样的服务器硬件能应对更多的连接。