3. Tong brother said netty series of Java BIO NIO AIO evolutionary history .md

nio

Hello, I am a brother Tong herein, this is the third in a series netty.

Welcome to my public from No. Tong brother read the source code systematic study of the source code & architecture knowledge.

Let me talk about two things

(1) article on Friday made repeated, is scheduled tasks set errors, give us interfering, here to say sorry.

Survey results before (2) out that talk about the case of higher number of votes, so the back of the article is to talk about the case, and then to expand the case to explain the components.

Brief introduction

The last chapter we introduced five models IO, in fact, Java supports only three of them, namely, BIO / NIO / AIO.

This article describes the evolutionary history of these three IO in Java, and to analyze the story behind them in terms of use.

Java BIO

BIO analytical concept

BIO, Blocking IO, blocking IO, which is Java's ancient product, there is something from birth (JDK 1.0).

BIO preparation and use of the data copy data from user space to kernel space two stages are blocked.

blocking-io

BIO Use Cases

public class EchoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            System.out.println("start accept");
            Socket socket = serverSocket.accept();
            System.out.println("new conn: " + socket.getRemoteSocketAddress());

            new Thread(()->{
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    String msg;
                    // 读取消息,本文来源公从号彤哥读源码
                    while ((msg = reader.readLine()) != null) {
                        if (msg.equalsIgnoreCase("quit")) {
                            reader.close();
                            socket.close();
                            break;
                        } else {
                            System.out.println("receive msg: " + msg);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Clients can use telnet to test, and you can use multiple telnet to test:

[c:\~]$ telnet 127.0.0.1 8080

Connecting to 127.0.0.1:8080...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
hello world
我是人才
quit
Connection closed by foreign host.

BIO is very simple to use, the service receives a link to start a thread to handle the connection of all requests.

bio_nio_aio

Therefore, BIO biggest drawback is a waste of resources, can only deal with a small number of connections, the number of threads increases linearly with the number of connections, connecting more threads more, until not withstand.

Java NIO

NIO concept resolve

NIO, New IO, JDK1.4 began to support internal IO model is based multiplexing.

multiplexing-io

Here is ambiguous, many people think Java's NIO is an acronym for Non-Blocking IO, in fact, not.

NIO using the plurality of data preparation phase of the connection will be blocked on select, copy data from kernel space to user space is still blocked.

Because the first stage itself is not connected in the blocking stage, it is usually for NIO can be seen as synchronous non-blocking IO.

NIO use cases

public class EchoServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        // 将accept事件绑定到selector上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 阻塞在select上
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            // 遍历selectKeys
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 如果是accept事件
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                    SocketChannel socketChannel = ssc.accept();
                    System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    // 如果是读取事件,本文来源公从号彤哥读源码
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    // 将数据读入到buffer中
                    int length = socketChannel.read(buffer);
                    if (length > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        // 将数据读入到byte数组中
                        buffer.get(bytes);

                        // 换行符会跟着消息一起传过来
                        String content = new String(bytes, "UTF-8").replace("\r\n", "");
                        if (content.equalsIgnoreCase("quit")) {
                            selectionKey.cancel();
                            socketChannel.close();
                        } else {
                            System.out.println("receive msg: " + content);
                        }
                    }
                }
                iterator.remove();
            }
        }
    }
}

Here, too, use telnet test, and you can use multiple telnet to test:

[c:\~]$ telnet 127.0.0.1 8080

Connecting to 127.0.0.1:8080...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
hello world
我是人才
quit
Connection closed by foreign host.

NIO's use a bit complicated, but one thread can handle many connections.

首先,需要注册一个ServerSocketChannel并把它注册到selector上并监听accept事件,然后accept到连接后会获取到SocketChannel,同样把SocketChannel也注册到selector上,但是监听的是read事件。

bio_nio_aio

NIO最大的优点,就是一个线程就可以处理大量的连接,缺点是不适合处理阻塞性任务,因为阻塞性任务会把这个线程占有着,其它连接的请求将得不到及时处理。

Java AIO

AIO概念介绍

AIO,Asynchronous IO,异步IO,JDK1.7开始支持,算是一种比较完美的IO,Windows下比较成熟,但Linux下还不太成熟。

asynchronous-io

使用异步IO则会在请求时立即返回,并在数据已准备且已拷贝到用户空间后进行回调处理,两个阶段都不会阻塞。

AIO使用案例

public class EchoServer {
    public static void main(String[] args) throws IOException {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 监听accept事件,本文来源公从号彤哥读源码
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                try {
                    System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
                    // 再次监听accept事件
                    serverSocketChannel.accept(null, this);

                    // 消息的处理
                    while (true) {
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        // 将数据读入到buffer中
                        Future<Integer> future = socketChannel.read(buffer);
                        if (future.get() > 0) {
                            buffer.flip();
                            byte[] bytes = new byte[buffer.remaining()];
                            // 将数据读入到byte数组中
                            buffer.get(bytes);

                            String content = new String(bytes, "UTF-8");
                            // 换行符会当成另一条消息传过来
                            if (content.equals("\r\n")) {
                                continue;
                            }
                            if (content.equalsIgnoreCase("quit")) {
                                socketChannel.close();
                                break;
                            } else {
                                System.out.println("receive msg: " + content);
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("failed");
            }
        });

        // 阻塞住主线程
        System.in.read();
    }
}

这里同样使用telnet测试,而且你可以使用多个telnet来测试:

[c:\~]$ telnet 127.0.0.1 8080

Connecting to 127.0.0.1:8080...
Connection established.
To escape to local shell, press 'Ctrl+Alt+]'.
hello world
我是人才
quit
Connection closed by foreign host.

AIO的使用方式不算太复杂,默认会启一组线程来处理用户的请求,而且如果在处理阻塞性任务,还会自动增加新的线程来处理其它连接的任务。

首先,创建一个AsynchronousServerSocketChannel并调用其accept方法,这一步相当于监听了accept事件,在收到accept事件后会获取到AsynchronousSocketChannel,然后就可以在回调方法completed()里面读取数据了,当然也要继续监听accept事件。

AIO最大的优点,就是少量的线程就可以处理大量的连接,而且可以处理阻塞性任务,但不能大量阻塞,否则线程数量会膨胀。

槽点

(1)三种IO的实现方式中对于换行符的处理竟然都不一样,BIO中不会把换行符带过来(其实是带过来了,因为用了readLine()方法,所以换行符没了),NIO中会把换行符加在消息末尾,AIO中会把换行符当成一条新的消息传过来,很神奇,为啥不统一处理呢,也很疑惑。

(2)JDK自带的ByteBuffer是一个难用的东西。

总结

In this paper we use the concept of two angles and introduced the BIO / NIO / AIO three kinds of IO models.

problem

It looks JDK implementation seems to be perfect, ah, why would Netty it?

Finally, the public is also welcome to come to my number from Tong brother read the source code systematic study of the source code & architecture knowledge.

code

Guess you like

Origin blog.51cto.com/14267003/2451409