1. 同步阻塞BIO
-
serversocket
负责绑定IP和Port,socket
负责发起连接操作。连接成功后,通过输入/输出流进行同步阻塞式通信。 - 传统请求-应答模型,Acceptor线程每接受一个请求,创建一个线程处理请求并返回。
2. 伪异步IO
- 后来考虑到高性能/高并发场景,演进了用线程池/消息队列实现1个或多个线程处理N个客户端。但底层通信机制仍是同步阻塞IO,故称作“伪异步”。线程池中的线程数量和队列大小可控,因此不会导致资源耗尽和宕机。
- 弊端:JDK的API文档中,对于InputStream和OutputStream中的read(byte[] b)和write(byte[] b)操作,都是同步阻塞的,亦即一直阻塞直到发生如下事件:
- 有数据可读。
- 读到数据末尾。
- 发生空指针或IO异常。
如果大量连接上来,前端只有一个Acceptor线程接入请求,那么,之前的请求在线程池中的队列(阻塞队列实现)中排队,队列满,入队操作阻塞,新请求将被拒绝。破解这一难题,NIO入场。
3. NIO编程
官方
New IO
,更多人接受Non-block IO
.与Socket和ServerSocket类似,NIO提供了SocketChannel和ServerSocketChannel套接字通道实现,且支持阻塞和非阻塞模式。
3.1. 缓冲区Buffer
在NIO中,所有的数据操作都是面对缓冲区的,从缓冲区读,往缓冲区写。实现原理是数组。
3.2. 通道Channel
通道与流的不同,在于通道是双向的,而流是单向的。通道可用于读、写、同时读写。同时支持阻塞和非阻塞模式。
3.3. 多路复用器Selector
多路选择器提供了选择已经就绪的任务的能力。Selector会不断轮询注册其上的Channel,如果某个Channel上有新的TCP连接、读、写事件,这个Channel就处于就绪状态,会被Selector轮询出来。JDK使用了epoll()代替了select()实现,没有最大连接句柄数1024/2048的限制,一般1GB内存支持100 000个连接。
4. AIO 编程
NIO2.0引入新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。是真正的异步非阻塞IO,对应unix的事件驱动IO(AIO),不需要Selector对其轮询即可实现异步读写。
5. 总结
5.1. 不选择原生NIO编程原因
- NIO类库和API非常负责,使用麻烦。
- 需具备其他技能做铺垫,如Java多线程编程,Reactor模式等。
- 可靠性不够,如客户端断连重连,网络闪断,半包读写,失败缓存等。
- JDK的NIO bug,如epoll bug,它会导致Selector空轮询,导致CPU使用率100%。
5.2. 为什么选择Netty
- API使用简单,开发门槛低。
- 功能强大,预置了多种编解码功能,支持主流协议。
- 定制能力强。
- 性能高。
- 成熟稳定,修复了已发现的所有NIO bug。
- 社区活跃,版本迭代周期短。
- 经过了大规模的商用考验。