Java-BIO

13次阅读

共计 1533 个字符,预计需要花费 4 分钟才能阅读完成。

引言

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

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

BIO

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

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 条件下,常采用一下编程模型:

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

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

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

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

根据经典的 BIO 编程模型,所有请求需要新建 Handler 线程处理。

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

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

总结

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

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

版权声明

本文作者:河北工业大学梦云智开发团队 – 张喜硕

正文完
 0