文章目录
- BIO、NIO、AIO的区别?
- 为什么不使用java 原生nio
- 哪些项目使用了netty
- BIO
- 阻塞I/O存在问题
- NIO(nonblocking IO)
- Java NIO channel(通道)、buffer、selector(选择器)
- AIO(Asynchronous I/O)
BIO、NIO、AIO的区别?
Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。
为什么不使用java 原生nio
使用 JDK 原生 NIO 进行网络编程确实存在一些挑战和不便:
- 需要手动管理 Selector、Channel 和 Buffer代码实现相对复杂,相比 ,Netty等框架,开发周期长,实现相同功能需要更多代码。
- Netty 提供更简洁的 API,性能和可靠性更有保障。推荐使用Netty等成熟NIO框架,可以显著降低开发复杂度。
哪些项目使用了netty
阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。
淘宝的消息中间件 RocketMQ 的消息生产者和消息消费者之间,采用 Netty 进行高性能、异步通信。
Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨节点通信,它的 Netty Service 基于 Netty 框架二次封装实现。
Elasticsearch(分布式搜索引擎)、gRPC(谷歌开源的RPC框架)、Spark(开源大数据计算框架)、分布式计算框架 Storm、并发应用和分布式应用 Akka。
Netty 基本是 Java 语言处理网络 IO 的首选框架,性能和稳定性都有保障,社区比较活跃,基于 Netty 进行二次定制服务成本小。
BIO
阻塞IO,即在读写数据的过程中会发生阻塞现象。
当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,操作系统就会将数据从内核空间拷贝到用户空间,并返回结果给用户线程,用户线程才解除阻塞状态。
(因为我们的用户程序只能获取用户空间的内存,无法直接获取内核空间的内存)
java1.4以前的io模型,一连接对一个线程。同步阻塞IO, 其实就是服务端创建一个ServerSocket, 然后就是客户端用一个Socket去连接服务端的那个ServerSocket, ServerSocket接收到了一个的连接请求就创建一个Socket和一个线程去跟那个Socket进行通讯。
阻塞I/O存在问题
一个使用传统阻塞I/O的系统,如果还是使用传统的一个请求对应一个线程这种模式,一旦有高并发的大量请求,就会有如下问题:
1、线程不够用, 就算使用了线程池复用线程也无济于事;
2、阻塞I/O模式下,会有大量的线程被阻塞,一直在等待数据,这个时候的线程被挂起,只能干等,CPU利用率很低,换句话说,系统的吞吐量差;
3、如果网络I/O堵塞或者有网络抖动或者网络故障等,线程的阻塞时间可能很长。整个系统也变的不可靠;
NIO(nonblocking IO)
当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时(数据未准备就绪),它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。
NIO 是可以做到用一个线程来处理多个操作的。假设有 10000 个请求过来,根据实际情况,可以分配50 或者 100 个线程来处理。不像之前的阻塞 IO 那样,非得分配 10000 个。
这里面优化BIO的核心就是,一个客户端并不是时时刻刻都有数据进行交互,没有必要死耗着一个线程不放,所以客户端选择了让线程歇一歇,只有客户端有相应的操作的时候才发起通知,创建一个线程来处理请求。
因为无论多少客户端都可以接入服务端,客户端接入并不会耗费一个线程,只会创建一个连接然后注册到selector上,你就可以去干其他你想干的其他事情了, 一个selector线程不断的轮询所有的socket连接,发现有事件了就通知你,然后你就启动一个线程处理一个请求即可,这个过程的话就是非阻塞的。但是这个处理的过程中,你还是要先读取数据,处理,再返回的,这是个同步的过程。
Java NIO channel(通道)、buffer、selector(选择器)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器。NIO的线程模型 对Socket发起的连接不需要每个都创建一个线程,完全可以使用一个Selector来多路复用监听N多个Channel是否有请求。
AIO(Asynchronous I/O)
通过AIO发起个文件IO操作之后,你立马就返回可以干别的事儿了,接下来你也不用管了,操作系统自己干完了IO之后,告诉你说ok了, 当你基于AIO的api去都写文件的时候, 当你发起一个请求之后,剩下的事情就是交给了操作系统,当读写完成后, 操作系统会来回调你的接口, 告诉你操作完成, 在这期间不需要等待, 也不需要去轮询判断操作系统完成的状态,你可以去干其他的事情。 同步就是自己还得主动去轮询操作系统,异步就是操作系统反过来通知你。所以来说, AIO就是异步非阻塞的。