文章目录
Java中的IO模型
BIO
BIO是同步阻塞模型,一个客户端连接对应一个处理线程。在BIO中,accept和read方法都是阻塞操作,如果没有连接请求,accept方法阻塞;如果无数据可读取,read方法阻塞。
NIO
NIO是同步非阻塞模型,服务端的一个线程可以处理多个请求,客户端发送的连接请求注册在多路复用器Selector上,服务端线程通过轮询多路复用器查看是否有IO请求,有则进行处理。
NIO的三大核心组件:
Buffer:用于存储数据,底层基于数组实现,针对8种基本类型提供了对应的缓冲区类。
Channel:用于进行数据传输,面向缓冲区进行操作,支持双向传输,数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中。
Selector:选择器,当向一个Selector中注册Channel后,Selector 内部的机制就可以自动不断地查询(Select)这些注册的Channel是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个Channel,也可以说管理多个网络连接,因此,Selector也被称为多路复用器。当某个Channel上面发生了读或者写事件,这个Channel就处于就绪状态,会被Selector监听到,然后通过SelectionKeys可以获取就绪Channel的集合,进行后续的I/O操作。
Epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
对BIO和NIO实现感兴趣朋友可以看看我的代码,是用Java实现的BIO/NIO java实现
AIO
AIO是异步非阻塞模型,一般用于连接数较多且连接时间较长的应用,在读写事件完成后由回调服务去通知程序启动线程进行处理。与NIO不同,当进行读写操作时,只需直接调用read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。
对于这些BIO,NIO,AIO它们都是Linux的五种IO模型,牵扯到同步异步,阻塞非阻塞概念大家可以看看一下我的另外一篇博客会有助于理解这篇博客同步、异步、阻塞与非阻塞
五种IO模型
IO模型的演化-从单线程Reactor模式到MS主从Reactor模式在到Netty
传统IO模式
对于传统的I/O通信方式来说,客户端连接到服务端,服务端接收客户端请求并响应的流程为:读取 -> 解码 -> 应用处理 -> 编码 -> 发送结果。服务端为每一个客户端连接新建一个线程,建立通道,从而处理后续的请求,也就是BIO的方式。
这种方式在客户端数量不断增加的情况下,对于连接和请求的响应会急剧下降,并且占用太多线程浪费资源,线程数量也不是没有上限的,会遇到各种瓶颈。虽然可以使用线程池进行优化,但是依然有诸多问题,比如在线程池中所有线程都在处理请求时,无法响应其他的客户端连接,每个客户端依旧需要专门的服务端线程来服务,即使此时客户端无请求,也处于阻塞状态无法释放。基于此,提出了基于事件驱动的Reactor模型。
Reactor模式
Reactor模式是基于事件驱动开发的,服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor模式也叫Dispatcher模式,即I/O多路复用统一监听事件,收到事件后分发(Dispatch给某进程或线程让线程进行IO操作),这是编写高性能网络服务器的必备技术之一。
Reactor模式以NIO为底层支持,核心组成部分包括Reactor和Handler:
- Reactor:Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对I/O事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人。
- Handlers:处理程序执行I/O事件要完成的实际事件,Reactor通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。类似于客户想要与之交谈的公司中的实际员工。
根据Reactor的数量和Handler线程数量,可以将Reactor分为三种模型:
- 单线程模型 (单Reactor单线程)
- 多线程模型 (单Reactor多线程)
- 主从多线程模型 (多Reactor多线程)
这三种模式的进化,是一种逐步分层的过程。
- 单Reactor单线程模式,服务器单线程,接受客户端连接,处理IO都在一个线程。因为是单线程当某个Handler阻塞时,会导致其他客户端的handler和accpetor都得不到执行,无法做到高性能,只适用于业务处理非常快速的场景,如redis读写操作。
- 单Reactor多线程将接受客户端连接放在服务器线程中,创建一个线程池,将每个客户端IO操作当做任务放入线程池的一个线程中进行处理。
- 多Reactor多线程是将连接事件也放入一个连接池,处理IO也是一个连接池中,两个连接池。
- Netty是在多Reactor多线程基础上进行了改进。
单Reactor单线程
Reactor内部通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立的事件,则由Acceptor处理,Acceptor通过accept接受连接,并创建一个Handler来处理连接后续的各种事件,如果是读写事件,直接调用连接对应的Handler来处理。
Handler完成read -> (decode -> compute -> encode) ->send的业务流程。
这种模型好处是简单,坏处却很明显,当某个Handler阻塞时,会导致其他客户端的handler和accpetor都得不到执行,无法做到高性能,只适用于业务处理非常快速的场景,如redis读写操作。
单Reactor多线程
主线程中,Reactor对象通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立事件,则由Acceptor处理,Acceptor通过accept接收连接,并创建一个Handler来处理后续事件,而Handler只负责响应事件,不进行业务操作,也就是只进行read读取数据和write写出数据,业务处理交给一个线程池进行处理。
线程池分配一个线程完成真正的业务处理,然后将响应结果交给主进程的Handler处理,Handler将结果send给client。
单Reactor承担所有事件的监听和响应,而当我们的服务端遇到大量的客户端同时进行连接,或者在请求连接时执行一些耗时操作,比如身份认证,权限检查等,这种瞬时的高并发就容易成为性能瓶颈。
多Reactor多线程(主从Reactor)
存在多个Reactor,每个Reactor都有自己的Selector选择器,线程和dispatch。
主线程中的mainReactor通过自己的Selector监控连接建立事件,收到事件后通过Accpetor接收,将新的连接分配给某个子线程。
子线程中的subReactor将mainReactor分配的连接加入连接队列中通过自己的Selector进行监听,并创建一个Handler用于处理后续事件。
Handler完成read -> 业务处理 -> send的完整业务流程。
关于Reactor,最权威的资料应该是Doug Lea大神的Scalable IO in Java,有兴趣的同学可以看看。
Netty
Netty相对于主从Reactor模式BossGroup相当于主Reactor,只不多主从里主Reactor单线程的负责接收连接事件。但是Netty将主Reactor变成了BossGroup线程池,池里有多个线程NioEventLoop里面都有一个Selector来监听连接事件。
Netty抽象了两组线程池BossGroup和WorkerGroup,其类型都是NioEventLoopGroup,BossGroup用来接受客户端发来的连接,WorkerGroup则负责对完成TCP三次握手的连接进行处理。
NioEventLoopGroup里面包含了多个NioEventLoop,管理NioEventLoop的生命周期。每个NioEventLoop中包含了一个NIO Selector、一个队列、一个线程;其中线程用来做轮询注册到Selector上的Channel的读写事件和对投递到队列里面的事件进行处理。
代码示例
单Reactor单线程
NIO多线程Reactor模式
- Server服务器端
public class ServerDemo1 {
private static final int PORT=10085;
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private ExecutorService threadpool; //线程池
public ServerDemo1() {
try{
this.selector = Selector.open();
this.serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server init success");
} catch (IOException e) {
e.printStackTrace();
}
this.threadpool = Executors.newFixedThreadPool(5);
}
public void listener(){
while (true){
try{
int select = selector.select();
if (select>0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
if (selectionKeys.isEmpty()){
continue;
}
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
handle(selectionKey);
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handle(SelectionKey selectionKey) {
SocketChannel clientchannel=null;
try{
if (selectionKey.isValid() && selectionKey.isAcceptable()){
clientchannel = serverSocketChannel.accept();
clientchannel.configureBlocking(false);
clientchannel.register(selector,SelectionKey.OP_READ);
System.out.println("一客户端创建连接");
} else if (selectionKey.isValid() && selectionKey.isReadable()){
clientchannel = (SocketChannel) selectionKey.channel();
threadpool.submit(new ServerHnadler(clientchannel));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ServerDemo1().listener();
}
}
- Server服务器端Handle
public class ServerHnadler implements Runnable {
private ByteBuffer recBuffer;
private ByteBuffer sendBuffer;
private SocketChannel clientchannel;
public ServerHnadler(SocketChannel clientchannel) {
this.clientchannel=clientchannel;
this.recBuffer=ByteBuffer.allocate(1024);
this.sendBuffer=ByteBuffer.allocate(1024);
}
@Override
public void run() {
recBuffer.clear();
sendBuffer.clear();
String sendmessage="服务器端收到";
try {
int read = clientchannel.read(recBuffer);
if (read > 0){
String recmessage = new String(recBuffer.array(), 0, read);
System.out.println("收到客户端消息"+clientchannel.getRemoteAddress()+"--"+recmessage);
sendBuffer.put(sendmessage.getBytes());
sendBuffer.flip();
clientchannel.write(sendBuffer);
System.out.println("服务器回复消息");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Client客户端
public class ClientDemo1 {
int port=10085;
String IP="127.0.0.1";
SocketChannel socketChannel;
ByteBuffer sendBuffer;
ByteBuffer recBuffer;
Selector selector;
public ClientDemo1() {
initClient();
}
private void initClient() {
try {
selector=Selector.open();
sendBuffer=ByteBuffer.allocate(4096);
recBuffer=ByteBuffer.allocate(4096);
socketChannel=SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT);
System.out.println("客户端请求连接");
socketChannel.connect(new InetSocketAddress(IP,port));
listener();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listener() throws IOException {
while (true){
int select = selector.select();
if (select > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
if (selectionKeys.isEmpty()){
continue;
}
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
handler(selectionKey);
iterator.remove();
}
}
}
}
private void handler(SelectionKey selectionKey) throws IOException {
if (selectionKey.isValid() && selectionKey.isConnectable()){
System.out.println("客户端已经开始连接");
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();//完成连接
String sendmessage = "你好,我是客户端";
sendBuffer.clear();
sendBuffer.put(sendmessage.getBytes());
sendBuffer.flip();
socketChannel.write(sendBuffer);
socketChannel.register(selector, SelectionKey.OP_READ);
}
}else if (selectionKey.isValid() && selectionKey.isReadable()){
recBuffer.clear();
int read = socketChannel.read(recBuffer);
if (read > 0){
String recv = new String(recBuffer.array(), 0, read);
System.out.println("客户端收到服务器的消息--"+recv);
}
}
}
public static void main(String[] args) {
new ClientDemo1();
}
}
主从Reactor模式
- Server服务器端
public class MSServer {
private final int PORT=10085;
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private ExecutorService threadpool;
public MSServer() {
this.threadpool = Executors.newFixedThreadPool(5);
try{
this.selector = Selector.open();
this.serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
}catch (IOException e){
e.printStackTrace();
}
System.out.println("server init success");
}
public void listener(){
try {
while (true){
int select = selector.select();
if (select > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
if (selectionKeys.isEmpty()){
continue;
}
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
handleAccept(selectionKey);
iterator.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 监听连接事件
* @param selectionKey
*/
private void handleAccept(SelectionKey selectionKey) {
SocketChannel clientchannel=null;
try {
if (selectionKey.isValid() && selectionKey.isAcceptable()){
System.out.println("一个客户端连接");
clientchannel= serverSocketChannel.accept();
MSServerHandle msServerHandle=new MSServerHandle();
Selector selectorson=msServerHandle.getSelector();//得到子线程的selector
clientchannel.configureBlocking(false);
clientchannel.register(selectorson,SelectionKey.OP_READ);//给子线程注册读
threadpool.submit(msServerHandle);//将作为任务放入线程池
}
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MSServer().listener();
}
}
- Server服务器端handle
public class MSServerHandle implements Runnable {
private Selector selector;
private ByteBuffer recBuffer;
private ByteBuffer sendBuffer;
public MSServerHandle() {
recBuffer=ByteBuffer.allocate(1024);
sendBuffer=ByteBuffer.allocate(1024);
try {
this.selector = Selector.open();
} catch (IOException e) {
e.printStackTrace();
}
}
public Selector getSelector() {
return selector;
}
@Override
public void run() {
try{
while (true){
int select = selector.select();
if (select > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
if (selectionKeys.isEmpty()){
continue;
}
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey nextkey = iterator.next();
handle(nextkey);
iterator.remove();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handle(SelectionKey nextkey) {
SocketChannel clientchannel;
try{
if (nextkey.isValid() && nextkey.isReadable()){
recBuffer.clear();
clientchannel= (SocketChannel) nextkey.channel();
int read = clientchannel.read(recBuffer);
if (read > 0){
String recmessage = new String(recBuffer.array(), 0, read);
System.out.println("收到客户端消息--"+recmessage);
}
//给客户端返回一个消息
sendBuffer.clear();
String sendmessage="服务器收到了消息";
sendBuffer.put(sendmessage.getBytes());
sendBuffer.flip();
clientchannel.write(sendBuffer);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Client客户端
public class MSClient {
int port=10085;
String IP="127.0.0.1";
SocketChannel socketChannel;
ByteBuffer sendBuffer;
ByteBuffer recBuffer;
Selector selector;
public MSClient() {
initClient();
}
private void initClient() {
try {
selector=Selector.open();
sendBuffer=ByteBuffer.allocate(4096);
recBuffer=ByteBuffer.allocate(4096);
socketChannel=SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT);
System.out.println("客户端请求连接");
socketChannel.connect(new InetSocketAddress(IP,port));
listener();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listener() throws IOException {
while (true){
int select = selector.select();
if (select > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
if (selectionKeys.isEmpty()){
continue;
}
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
handler(selectionKey);
iterator.remove();
}
}
}
}
private void handler(SelectionKey selectionKey) throws IOException {
if (selectionKey.isValid() && selectionKey.isConnectable()){
System.out.println("客户端已经开始连接");
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();//完成连接
String sendmessage = "你好,我是客户端";
sendBuffer.clear();
sendBuffer.put(sendmessage.getBytes());
sendBuffer.flip();
socketChannel.write(sendBuffer);
socketChannel.register(selector, SelectionKey.OP_READ);
}
}else if (selectionKey.isValid() && selectionKey.isReadable()){
recBuffer.clear();
int read = socketChannel.read(recBuffer);
if (read > 0){
String recv = new String(recBuffer.array(), 0, read);
System.out.println("客户端收到服务器的消息--"+recv);
}
}
}
public static void main(String[] args) {
new MSClient();
}
}