1. 程式人生 > >3. 彤哥說netty系列之Java BIO NIO AIO進化史

3. 彤哥說netty系列之Java BIO NIO AIO進化史

你好,我是彤哥,本篇是netty系列的第三篇。

簡介

上一章我們介紹了IO的五種模型,實際上Java只支援其中的三種,即BIO/NIO/AIO。

本文將介紹Java中這三種IO的進化史,並從使用的角度剖析它們背後的故事。

Java BIO

BIO概念解析

BIO,Blocking IO,阻塞IO,它是Java的上古產品,自出生就有的東西(JDK 1.0)。

使用BIO則資料準備和資料從核心空間拷貝到使用者空間兩個階段都是阻塞的。

BIO使用案例

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();
        }
    }
}

客戶端可以使用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.

BIO的使用方式非常簡單,服務端接收到一個連線就啟動一個執行緒來處理這個連線的所有請求。

所以,BIO最大的缺點就是浪費資源,只能處理少量的連線,執行緒數隨著連線數線性增加,連線越多執行緒越多,直到抗不住。

Java NIO

NIO概念解析

NIO,New IO,JDK1.4開始支援,內部是基於多路複用的IO模型。

這裡有個歧義,很多人認為Java的NIO是Non-Blocking IO的縮寫,其實並不是。

使用NIO則多條連線的資料準備階段會阻塞在select上,資料從核心空間拷貝到使用者空間依然是阻塞的。

因為第一階段並不是連線本身處於阻塞階段,所以通常來說NIO也可以看作是同步非阻塞IO。

NIO使用案例

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();
            }
        }
    }
}

這裡同樣使用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.

NIO的使用方式就有點複雜了,但是一個執行緒就可以處理很多連線。

首先,需要註冊一個ServerSocketChannel並把它註冊到selector上並監聽accept事件,然後accept到連線後會獲取到SocketChannel,同樣把SocketChannel也註冊到selector上,但是監聽的是read事件。

NIO最大的優點,就是一個執行緒就可以處理大量的連線,缺點是不適合處理阻塞性任務,因為阻塞性任務會把這個執行緒佔有著,其它連線的請求將得不到及時處理。

Java AIO

AIO概念介紹

AIO,Asynchronous IO,非同步IO,JDK1.7開始支援,算是一種比較完美的IO,Windows下比較成熟,但Linux下還不太成熟。

使用非同步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是一個難用的東西。

總結

本文我們從概念和使用兩個角度分別介紹了BIO/NIO/AIO三種IO模型。

問題

看起來JDK的實現似乎很完美啊,為什麼還會有Netty呢?

最後,也歡迎來我的公從號彤哥讀原始碼系統地學習原始碼&架構的知識。

相關推薦

3. netty系列Java BIO NIO AIO進化史

你好,我是彤哥,本篇是netty系列的第三篇。 簡介 上一章我們介紹了IO的五種模型,實際上Java只支援其中的三種,即BIO/NIO/AIO。 本文將介紹Java中這三種IO的進化史,並從使用的角度剖析它們背後的故事。 Java BIO BIO概念解析 BIO,Blocking IO,阻塞IO,它是Ja

4. netty系列Java NIO實現群聊(自己跟自己聊上癮了)

你好,我是彤哥,本篇是netty系列的第四篇。 歡迎來我的公從號彤哥讀原始碼系統地學習原始碼&架構的知識。 簡介 上一章我們一起學習了Java中的BIO/NIO/AIO的故事,本章將帶著大家一起使用純純的NIO實現一個越聊越上癮的“群聊系統”。 業務邏輯分析 首先,我們先來分析一下群聊的功能點:

5. netty系列Java NIO核心元件Channel

你好,我是彤哥,本篇是netty系列的第五篇。 簡介 上一章我們一起學習瞭如何使用Java原生NIO實現群聊系統,這章我們一起來看看Java NIO的核心元件之一——Channel。 思維轉變 首先,我想說的最重要的一個點是,學習NIO思維一定要從BIO那種一個連線一個執行緒的模式轉變成多個連線(Chan

6. netty系列Java NIO核心元件Buffer

——日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇。 簡介 上一章我們一起學習了Java NIO的核心元件Channel,它可以看作是實體與實體之間的連線,而且需要與Buffer互動,這一章我們就來學習一下Buffer的特性。 概念 Buffer用於與Channel互動時使用,通過上一

7. netty系列Java NIO核心元件Selector

——日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第七篇。 簡介 上一章我們一起學習了Java NIO的核心元件Buffer,它通常跟Channel一起使用,但是它們在網路IO中又該如何使用呢,今天我們將一起學習另一個NIO核心元件——Selector,沒有它可以說就幹不起來網路IO。 概念

java bio nio aio

流程圖: BIO BIO 全稱Block-IO 是一種阻塞同步的通訊模式。我們常說的Stock IO 一般指的是BIO。是一個比較傳統的通訊方式,模式簡單,使用方便。但併發處理能力低,通訊耗時,依賴網速。 BIO 設計原理: 伺服器通過一個Acceptor執行緒負責監

JAVA BIO,NIO,AIO詳解(附程式碼實現)

這幾天在看面試的東西,可能是自己比較笨,花了快兩天的時間才理清楚。特此記錄一下。 首先我們要理解的一個很重要概念是,客戶端連線和傳送資料是分開的,連線不代表立馬會傳輸資料。 說說BIO,NIO,AIO到底是什麼東西 BIO:同步堵塞 NIO:非同步堵塞 AIO:非同步非堵塞

Java BIO, NIO, AIO 簡單理解

1, 同步 VS. 非同步 比較的是當某種事件發生時呼叫處理函式的方式, 同步:應用程式主動呼叫處理函式, 非同步:作業系統呼叫應用程式的處理函式(也叫回調函式),對應用程式而言是被動過程。 2, 阻塞 VS. 非阻塞 阻塞:應用程式發起IO請求,IO請求(假設是網路上的

Java IO模式】Java BIO NIO AIO總結

一、同步與非同步、阻塞與非阻塞 1、同步與非同步 同步與非同步的區別在於,資料從核心空間拷貝到使用者空間是否由使用者執行緒完成。 – 對於同步來說,分阻塞和非阻塞兩種。阻塞的情況,一個執行緒維護一個連結,該執行緒完成資料的讀寫與處理的全部過程

2. netty系列IO的五種模型

你好,我是彤哥,本篇是netty系列的第二篇。 簡介 本文將介紹linux中的五種IO模型,同時也會介紹阻塞/非阻塞與同步/非同步的區別。 何為IO模型 對於一次IO操作,資料會先拷貝到核心空間中,然後再從核心空間拷貝到使用者空間中,所以一次read操作,會經歷兩個階段: (1)等待資料準備 (2)資料從

opensslBIO系列6---BIO的IO操作函數

長度 沒有 ons lin mail crypto 可能 說明文 作者 BIO的IO操作函數 ---依據openssl doc/crypto/bio/bio_read.pod翻譯和自己的理解寫成 (作者:DragonKing Mail:[ema

Java Killer系列Java經典面試套路講解

Java Killer系列之Java經典面試套路講解 java編程語言是目前應用較為廣泛的一門計算機編程語言,目前java市場需求量有增無減。java作為目前IT軟件開發行業的重要技術之一,人才市場出現大量缺口,所以從事java相關工作,還是非常有前景的。

nopCommerce 3.9 大波浪系列 global.asax

ring ntc 開發 idt disable pre ren ins 未處理 一.nop的global.asax文件 nop3.9基於ASP.NET MVC 5框架開發,而ASP.NET MVC中global.asax文件包含全局應用程序事件的事件處理程序,它響應應用程序

nopCommerce 3.9 大波浪系列 引擎 NopEngine

provide float 框架 blog exp splay activator 註入 title 本章涉及到的內容如下 1.EngineContext初始化IEngine實例 2.Autofac依賴註入初始化 3.AutoMapper框架初始化 4.啟動任務初始化

nopCommerce 3.9 大波浪系列 可退款的支付寶插件(上)

則無 res oschina lin 地址 放置 訂單狀態 mage 代碼 一.簡介 nop通過插件機制可以支持更多的支付擴展,我們通過編寫支持退款的支付寶插件來更好的理解支付插件的擴展。 先分享下支付寶插件源碼點擊下載,由於時間原因,本篇只介紹使用

nopCommerce 3.9 大波浪系列 微信公眾平臺登錄插件

authent verify rip state link value eat round 掃描 一.簡介 nop支持第三方登錄授權擴展,本篇通過編寫微信公眾平臺登錄插件進一步了解nop授權登錄的開發過程。 微信公眾平臺、微信開放平臺使用場景不一樣,

java並發bio nio aio

image 進行 nbsp ID str 系統 同步非阻塞 src lin   最近在進行tomcat優化,發現tomcat connector並發支持bio nio apr,發現想要理解tomcat並發離不開java io的理解。所有本文先探討java對io的支持。jav

Java虛擬機系列Java內存結構簡介

內存空間 指示器 私有 以及 並且 內存區域 在服務器 規範 基礎 本文我們將講解Java虛擬機中各個區域以及各個區域的作用。 一.程序計數器什麽是程序計數器,有什麽作用?程序技術器是一塊比較小的內存區域,主要當做是線程中所執行的字節碼的行號指示器,字節碼解釋器工作時就是通

Beam編程系列Java SDK Quickstart(官網的推薦步驟)

rate start mark http 單獨 org 托管 pipe bucket   不多說,直接上幹貨! https://beam.apache.org/get-started/beam-overview/

Netty序章BIO NIO AIO演變

升級版 port amr gist 文件操作 -i cti 升級 channel Netty序章之BIO NIO AIO演變 Netty是一個提供異步事件驅動的網絡應用框架,用以快速開發高性能、高可靠的網絡服務器和客戶端程序。Netty簡化了網絡程序的開發,是很多框架和公司