Netty的由来之阻塞I/O模型BIO

针对同步阻塞I/O模型效率底下的问题,java在2002年的JDK1.4之后引入了非阻塞I/O模型,但是操作比较复杂,Netty就是封装了底层非阻塞I/O操作而给用户提供可以相对简单使用的API库。

java早期版本对I/O支持不完善,性能低下,主要问题如下:

  • 无数据缓冲区,I/O性能低下
  • 没有C/C++中Channel概念,只有输入和输出流
  • 同步阻塞式I/O通信(BIO),通常会导致通信线程被长时间阻塞
  • 支持的字符集有限,硬件可移植性不好

Linux网络I/O模型

linux内核将所有设备都看做文件来操作,通过文件描述符file desvriptor来对文件进行相应的操作,fd就是一个数字,指向内核中的一个结构体。

Unix提供了5中I/O模型:

  • 阻塞I/O模型
  • 非阻塞I/O模型
  • I/O复用模型
  • 信号驱动I/O模型
  • 异步I/O

其中这里着重介绍一下异步I/O,首先通过一个图片了解一下异步I/O操作的使用例子:
此处输入图片的描述

用户应用层告诉内核某个操作后,不是像阻塞I/O一样阻塞等待内核完成操作后返回数据,而是立即返回,让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。

这与信号驱动型是有区别的,信号驱动型业务流程是这样的:
1. 首先开启信号驱动I/O
2. 通过系统调用sigaxtion执行一个信号处理函数(这个函数调用立即返回,进程继续工作,这是非阻塞的
3. 内核根据用户的请求开始等待数据,数据准备好后通过信号通知用户
4. 用户通过系统调用将数据进行拷贝,完成后返回一个结果。

用一个图表示:
信号驱动型业务流程

BIO

BIO通信模型图如下:
BIO通信模型
BIO通信模型的服务端通常由一个独立的Acceptor线程负责监听客户端的连接,接收到客户端的请求后为每个客户端创建一个新的线程进行链路处理,处理完成后通过输出流返回应答给客户端,然后销毁线程。这是典型的一请求一应答通信模型

该模型的缺点是缺少弹性伸缩能力,客户端访访问量巨大时,服务端线程和客户端并发访问数是1:1的正比关系。java线程是java虚拟机非常宝贵的资源,线程占用过多时,系统的性能会几句下降,当系统发生线程堆栈溢出、创建新线程失败时最终会导致宕机或者僵死的结果,不能对外提供服务。

下面以一个客户端获取当前时间的例子对BIO通信进行分析:

  • 服务端程序(TimeServer
package time.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TimeServer {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int port = 8080;
        if(args != null && args.length > 0){
            port = Integer.valueOf(args[0]);
        }
        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
            System.out.println("The time server is start in port" + port);
            Socket socket = null;
            while(true){
                socket = server.accept();
                new Thread(new TimeServerHandler(socket)).start();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(server != null){
                System.out.println("The time server close");
                try {
                    server.close();
                    server = null;
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

}

其中TimeServerHandler是新创建的一个java线程,用来处理客户端发来的请求,注意:一个线程只能处理客户端的一个请求TimeServerHandler的具体代码如下:

package time.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class TimeServerHandler implements Runnable {

    private Socket socket;

    public TimeServerHandler(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(
                    this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(), true);
            String currentTime = null;
            String body = null;
            while(true){
                body = in.readLine();
                if(body == null)
                    break;
                System.out.println("The time server receive order:" + body);
                currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new
                        java.util.Date(
                                System.currentTimeMillis()).toString() : "BAD ORDER";
                out.println(currentTime);
            }
        } catch (Exception e) {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                in = null;
        }
            if(out != null){
                out.close();
                out = null;
            }
            if(this.socket != null){
                try {
                    this.socket.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                this.socket = null;
            }
        } 

    }

}

其实TimeServerHandler主要负责处理客户端发来的请求,如果是客户端发来的是“QUERY TIME ORDER”,则把当前时间返回给客户端,这是通过PrintWriter操作流来实现的,具体原理请查看 的概念和操作。线程最后自动销毁,又java虚拟机回收。

客户端通过Socket创建、发送查询时间服务器的指令,然后将服务器的响应打印出来,随后关闭链接,释放资源。

package time.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class TimeClient {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int port = 8080;
        if(args != null && args.length > 0){
            port = Integer.valueOf(args[0]);
        }
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            socket = new Socket("127.0.0.1", port);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
            out.println("QUERY TIME ORDER");
            System.out.println("Send order 2 server succeed");
            String resp = in.readLine();
            System.out.println("Now is:" + resp);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
                out = null;
            }

            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
//              in = null;
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                socket = null;
            }
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值