IO NIO框架
这里主要涉及到IO多路复用
# 基本概念介绍
BIO 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
ServerSocket 负责绑定 IP 地址,启动监听端口;Socket 负责发起连接操作,连接成功后,双方通过输入和输出流进行同步阻塞通信。采用 BIO 通信模型的 Server,通常由一个独立的 Acceptor 线程负责监听 Client 端的连接,它接受到 Client 端连接请求后为每个 Client 创建一个新的线程进行处理,处理完之后,通过输出流返回给 Client 端,线程销毁,过程如下图所示
这个模型最大的问题是:
- 缺乏扩展性,不能处理高性能、高并发场景,线程是 JVM 中非常宝贵的资源,当线程数膨胀后,系统的性能就会急剧下降,随着并发访问量的继续增大,系统就会出现线程堆栈溢出、创建新线程失败等问题,导致 Server 不能对外提供服务。
NIO 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
与 Socket 类和 ServerSocket 类相对应,NIO 也提供了 SocketChannel 和 ServerSocketChannel 两种不同套接字通道的实现,它们都支持阻塞和非阻塞两种模式。一般来说,低负载、低并发的应用程序可以选择同步阻塞 IO 以降低复杂度,但是高负载、高并发的网络应用,需要使用 NIO 的非阻塞模式进行开发。
在 NIO 中有三种非常重要的概念:
- 缓冲区(buffer):本质上是一个数组,它包含一些要读写的数据;
- 通道(channel):是一个通道,通过它读写数据,类似于自来水管;
- 多路复用器(selector):用于选择已经就绪的任务,selector 会轮询注册在其上的 channel,选出已经就绪的 channel。
- Buffer:是缓冲区,任何时候访问 NIO 数据,都是通过 Buffer 进行;
- Channel:通过它读写 Buffer 中的数据,可以用于读、写或同时读写;
- Selector:多路复用器,Selector 不断轮询注册在其上的 Channel,如果某个 Channel 有新的 TCP 链接接入、读和写事件,这个 Channel 就处于就绪状态,会被 Selector 轮组出来,然后通过
SelectionKey()
可以获取就绪 Channel 的集合,进行后续的 IO 操作。
关于 Java NIO,有两种最常见的使用方式:
- 使用原生的 Java NIO(如 Kafka);
- 使用 Netty(Hadoop 的 RPC 框架 Avro 底层使用 Netty 做通信框架)。
在实际使用中,推荐第二种,使用 Netty 将会大大提高开发效率,后续会写篇关于 Netty 的文章,介绍一下 Netty 的具体内容,这里使用一个基于 Java 原生 NIO API 的小示例,讲述一下 NIO 的使用方法。
AIO 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理.AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
参考:
谈一谈 Java IO 模型 | Matt's Blog (matt33.com) (opens new window)
# Netty
Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。
Netty (opens new window)为什么没有用JDK自带的多路复用器(没答上来,提示JDK空轮训BUG)聊了聊Netty (opens new window)前身
Netty面试题(2020最新版)_ThinkWon的博客-CSDN博客_netty面试题 (opens new window)
# IO流
# Java 中 IO 流分为几种?
- 按照流的流向分,可以分为输入流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的角色划分为节点流和处理流。
# 既然有了字节流为什么还要有字符流?
问题本质想问:不管是文件读写还是网络发送接收,信息的最小存储单元都是字节,那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
回答:字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。