Java BIO

引言

Unix网络编程领域中,IO模型一直是十分重要的话题。而且在去学习RedisNginxNetty等底层原理时,对于高并发的处理,基本都用到了IO模型的概念。java

IO模型分为阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO,本文就其中最基础的阻塞式IO进行讲解。git

BIO

BIOBlocking IO,阻塞IO,对应java.io包。github

Java 1.4以前,提供了java.io包,阻塞IO编程模型。编程

假设咱们须要在Socket(运输层TCP)的基础上实现一个HTTP服务器,HTTP服务器网络编程采用阻塞IO实现,这里使用经常使用的输入输出流BufferedReaderBufferedWriter服务器

请看以下示例代码,输入输出流采用经典的装饰器模式:网络

ServerSocket serverSocket = null;
try {
    serverSocket = new ServerSocket(SocketConstant.DEFAULT_PORT);
    System.out.println("服务器启动于: " + SocketConstant.DEFAULT_PORT);

    while (true) {
        Socket socket = serverSocket.accept();
        System.out.println("客户端[" + socket.getPort() + "]发起链接");

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        String msg;
        while ((msg = reader.readLine()) != null) {
            // 处理网络请求
        }
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    // 回收资源
}

问题出在reader读取数据时,代码以下:多线程

String msg;
while ((msg = reader.readLine()) != null) {
    // 处理网络请求
}

由于采用了阻塞IOBufferedReader进行数据读取,因此假设网络拥塞的状况下,该TCP链接迟迟没有数据发送,线程会一直被阻塞,因示例代码采用单线程模型,任务变成串行处理,没法继续处理其余请求。并发

因此在BIO条件下,常采用一下编程模型:异步

image.png

为了保证主线程不被阻塞,服务器能正常接受请求,采用多线程方式解决。socket

主线程Acceptor不负责具体请求的处理,只负责接受请求,并建立相应的Handler线程进行请求处理,全部阻塞发生在Handler线程中,不影响主线程接受其余任务。

不要在主进程/主线程中处理任务的设计理念是值得学习的,主进程/主线程一旦挂了,整个节点都崩溃了,代价很大。

以下图所示,Nginx中分为主进程和工做进程,主进程负责任务分发,工做进程负责任务处理,若是工做进程崩溃了,主进程再从新fork工做进程,进行任务处理,整个节点依然可用。

image.png

根据经典的BIO编程模型,全部请求须要新建Handler线程处理。

new Thread(new Handler(socket)).start();

该模型存在一个致命的问题,当高并发时,形成系统中存在太多的线程,线程运行时的上下文频繁切换形成额外开销,给系统形成严重负担。

总结

学院换了副主任,答辩及论文格式规定有所调整,目前还在改格式。

之后会就NIOIO多路复用等经常使用模型进行学习。

版权声明

本文做者: 河北工业大学梦云智开发团队 - 张喜硕
相关文章
相关标签/搜索