Scalable IO in Java 学习笔记
Scalable IO in Java
Java NIO浅析 <- 看这个就好,下面不用看=.=
网络服务的基本结构
传统BIO模式
BIO模式下,socket的accept(),read(),write()方法都是同步阻塞的,即一个线程在等待客户端输入时,一直是处于阻塞状态的。为了能同时服务多个客户端,以及增加CPU的利用率,我们需要为每个连接单独开一个线程进行处理,由此会带来以下这些问题
- 线程创建及销毁成本很高(可以通过线程池缓解)
- 线程额外的内存开销,例如在没有显式设置
-Xss
时,每个Java线程都会默认占用1MB的栈空间 - 线程切换成本,当线程数过高时,大量CPU时间会被浪费在context switch上
NIO的工作方式
java.nio提供了两个基本特性
- 非阻塞的读写(Channel)
- 例如调用read()时,有数据就读取并返回,如果没有数据直接返回,不像BIO会一直阻塞到读取到数据为止
- 根据IO事件分派任务(Selector)
- 操作系统会将Channel上的IO状态变化主动推送给我们,例如可读、可写、有新连接等
- 具体实现依赖操作系统(例如Linux 2.6之后是O(1)的epoll,之前是O(n)的select)
依据这两个特性,我们就可以用事件驱动(event-driven)的方式,用一个线程来服务多个连接了。Reactor模式就是事件驱动模式的一种实现。
NIO中的基本概念
- Channels
- 支持非阻塞读写的连接,可以是文件,网络等等
- Buffers
- Channels读写时用到的对象,底层是数组
- Selectors
- 获取IO事件
- SelectionKeys
- 维护selector和channel之间的绑定关系,包括注册感兴趣的事件及绑定处理器
- IO事件
- OP_ACCEPT
- 服务端收到一个连接请求
- OP_CONNECT
- 客户端发起连接
- OP_READ
- 当OS的读缓冲区中有数据可读
- OP_WRITE
- 当OS的写缓冲区中有空闲的空间
- OP_ACCEPT
Reactor模式
单线程版本的Reactor
- Reactor
- 响应IO事件,分派任务给合适的handler
- Handler
- 执行非阻塞的操作,例如Acceptor,Reader,Sender等
Reactor
|
|
Handler
|
|
ByteBuffer cheatsheet
Caveat
- 当并发连接数不高时,NIO并没有显著性能优势。
- 使用事件驱动模式编程难度更高,代码需要拆分为多个non-blocking actions,同时需要小心维护服务的逻辑状态。
- 推荐使用成熟的NIO框架,如Netty,MINA
TODO
多线程版本的Reactor
了解AIO及Proactor模式