---
# System prepended metadata

title: AIO、BIO、NIO，NETTY框架

---

# AIO、BIO、NIO，NETTY框架

I/O Models

- Blocking I/O, BIO
- Non-Blocking I/O, NIO
- Asynchronous I/O, AIO

## I/O模型的演變

引用來源: https://alibaba-cloud.medium.com/essential-technologies-for-java-developers-i-o-and-netty-ec765676fd21

### 傳統I/O模型

對於傳統的 I/O 操作，用戶端連接到伺服器，它們通過以下過程相互通信：客戶端發送請求，伺服器讀取、解碼、計算和編碼請求，然後向用戶端發送回應。伺服器為每個用戶端連接創建一個線程和一個通道，然後處理後續請求（在 BIO 模式下）。

在此模式下，當客戶端數量增加時，對連接請求的回應會急劇減少，並且佔用過多的線程，從而浪費資源。在這種情況下，由於線程數量有限而出現瓶頸。雖然線程池可用於優化，但仍然存在許多問題。例如，當線程池中的所有線程都在處理請求時，它們無法回應來自其他客戶端的連接請求。每個用戶端仍然需要來自專用伺服器線程的服務。即使用戶端在給定時間沒有請求，線程也會被阻止且無法釋放。為了解決這個問題，提出了事件驅動的反應堆模型。

![](https://hackmd.io/_uploads/BJ_znorAn.png)

### Reactor Model




## 同步、非同步、阻塞、非阻塞
Synchronous (同步)、Asynchronous (非同步)

在這裡意義為在執行IO動作時, 是由jvm處理或是交由OS處理來區分. 若為jvm處理則為Synchronous, 否則為Asynchronous.

Block(阻塞)、Non-Block(非阻塞)

依據"進行IO操作時是否需要等待"作為區分. 若操作IO時當下的Thread需要等待則為Block, 否則為Non-Block.

![](https://hackmd.io/_uploads/rk5rYsBC2.png)
![](https://hackmd.io/_uploads/SJxjqoHRh.png)


## BIO
![](https://hackmd.io/_uploads/HJFnKjBC3.png)



## BIO 實戰
```java
import java.io.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentBlockingIOExample {

    public static void main(String[] args) {
        int numThreads = 5; // 想要建立的線程數量
        ExecutorService executorService = Executors.newFixedThreadPool(numThreads);

        for (int i = 0; i < numThreads; i++) {
            executorService.execute(new IOOperation());
        }

        executorService.shutdown();
    }

    static class IOOperation implements Runnable {
        @Override
        public void run() {
            String fileName = "example.txt";
            try {
                // 建立IO
                new io
                // 開始讀取文件內容
                io.read
                // 關閉資源
                io.close
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

```
## NIO實戰
通道可以实现双向读写，比如说用RandomAccessFile类获取文件读写，调用RandomAccessFile.getChannel()方法，获取的就是读写双向的通道
```java
import java.io.IOException;
import java.io.RandomAccessFile;

public class NonBlockingFileIOExample {

    public static void main(String[] args) {
        String fileName = "example.txt";

        Thread readerThread = new Thread(() -> {
            try {
                // 打開文件並進行非阻塞讀取
                RandomAccessFile file = new RandomAccessFile(fileName, "r");

                byte[] buffer = new byte[1024];
                int bytesRead;

                while ((bytesRead = file.read(buffer)) != -1) {
                    String data = new String(buffer, 0, bytesRead);
                    System.out.println("讀取的內容: " + data);
                }

                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        readerThread.start();
    }
}


```
## AIO實戰
```java
public class AioServer {
 
    public static void main(String[] args) throws IOException {
        System.out.println(Thread.currentThread().getName() + " AioServer start");
        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open()
                .bind(new InetSocketAddress("127.0.0.1", 8080));
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
 
            @Override
            public void completed(AsynchronousSocketChannel clientChannel, Void attachment) {
                System.out.println(Thread.currentThread().getName() + " client is connected");
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                clientChannel.read(buffer, buffer, new ClientHandler());
            }
 
            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("accept fail");
            }
        });
        System.in.read();
    }
}
 
public class ClientHandler implements CompletionHandler<Integer, ByteBuffer> {
    @Override
    public void completed(Integer result, ByteBuffer buffer) {
        buffer.flip();
        byte [] data = new byte[buffer.remaining()];
        buffer.get(data);
        System.out.println(Thread.currentThread().getName() + " received:"  + new String(data, StandardCharsets.UTF_8));
    }
 
    @Override
    public void failed(Throwable exc, ByteBuffer buffer) {
 
    }
}


```

## NIO

### NIO主要就是為了解決BIO進行IO操作時Blocak所造成效率不佳的問題。

NIO是基於Reactor(事件驅動), <font color="red">也就是有IO事件發生時再來處理就好.</font> 因此導入了一個Selector概念, 將連線的發生、IO資料輸入/輸出都視為"事件(Event)", 在此只要操作Selector取出事件作相對應的事情即可. 這概念可以想像成底層將這些"事件(Event)"都塞到了一個queue裡, 然後操作這個Selector不斷的從這個queue取出這些event進行處理即可. 但因為所有的事情都視為event, 所以所有client的連線事件都會混在一起被select出來處理, 因此需要比較複雜的程式判斷才不至於混淆。

操作流程大致上是:
```
1.操作selector取出event

2-1.若是connect事件需要完成TCP三方交握, 完成後會取得一個與這個Client專屬的Channel, 再將此Channel註冊回Selector, Selector將會監聽此Channel Input事件的發生。

2-2.若是Input事件, 則可以透過ByteBuffer將Input的內容取出, 若當下ByteBuffer不足以取出所有Input內容, 那麼下次select一樣有會此次Channel的Input事件被取出, 直到程式將Input全部取完。

2-3.上述的操作皆可以使用與select同個Thread做事, 或交由Thread Pool去處理. 若使用同個來Thread處理Input事件, 那概念就會像是Node.js單線程操作一樣, 遇到IO時一樣交由底層所控制的Thread去執行, 當下的Thread可以回來繼續處理業務邏輯.

3.回頭繼續取出事件
```
NIO在IO操作時引入了兩個新個概念,"Channel"與"ByteBuffer".

1. "Channel": 
    對應BIO的InputStream, OutputStream. BIO的I/O Stream是單向操作, 而NIO的Channel是雙向操作, 讀/寫均透過此物件.

2. "ByteBuffer": 
    NIO在進行IO操作時均要使用ByteBuffer給予Channel. 概念就是要output的內容時會先塞到ByteBuffer, 再丟給Channel去output(底層將調用別的Thread進行output動作); 而Input事件發生時, 底層會先將內容暫存, 我們程式一樣透過ByteBuffer取用. 這是我們程式與底層IO間的重要橋樑, 讓我們可以以非同步狀態操作IO, 進而讓Thread不再因為等待IO而偷懶.
![](https://hackmd.io/_uploads/ryCYjsHA2.png)

海底撈很好喫，但是經常要排隊。我們就以生活中的這個例子進行講解。

A 顧客去喫海底撈，就這樣乾坐着等了一小時，然後纔開始喫火鍋。(BIO)

B 顧客去喫海底撈，他一看要等挺久，於是去逛商場，每次逛一會就跑回來看有沒有排到他。於是他最後既購了物，又喫上海底撈了。（NIO）

C 顧客去喫海底撈，由於他是高級會員，所以店長說，你去商場隨便玩吧，等下有位置，我立馬打電話給你。於是 C 顧客不用幹坐着等，也不用每過一會兒就跑回來看有沒有等到，最後也喫上了海底撈（AIO）

哪種方式更有效率呢？是不是一目瞭然呢？

## AIO

![](https://hackmd.io/_uploads/SkroCjrC3.png)

AIO（非同步 I/O）是 Java 中的一種非同步 I/O 模型，它在 Java 7 及以後的版本中引入，旨在改善傳統的阻塞 I/O（BIO）和非阻塞 I/O（NIO）模型的一些限制。

AIO主要與NIO最大的差別是IO是否由jvm處理. NIO在處理IO的時候, 仍然是由jvm處理, 也就是NIO底層仍然會調用Thread進行處理; 而AIO在處理IO時是委託OS進行處理, 不占用jvm資源. 而IO委託OS進行處理, 就意味著OS需要支援IO非同步操作

AIO的操作流程完全基於callback function(也是listener的概念), 在操作接受連線、讀取input等動作時, 是準備一個callback給channel, 在底層準備好這些事情時將會呼叫這些callback相對應的method, 就不需要像NIO一樣需要占用一個Thread不斷select事件來處理. 而在操作output動作時, 則是跟NIO一樣準備好ByteBuffer丟給Channel進行output, 並同時給定一個callback, 底層將在完成output事情後呼叫對應的method.


工作原理：

1. 在 AIO 模型中，與傳統 I/O 不同，不會阻塞線程等待數據可用或數據寫入。相反，AIO 使用回調機制，當數據可用時，操作系統會通知應用程序，應用程序負責處理數據。
2. AIO 主要通過兩種非同步事件來完成：讀取完成事件和寫入完成事件。應用程序通過註冊回調函數來處理這些事件。
3. 當數據可讀取時，操作系統會調用註冊的讀取完成回調函數來處理數據。同樣，當可以進行寫入操作時，操作系統會調用寫入完成回調函數。

特點：

1. 非阻塞： AIO 模型是非阻塞的，它允許應用程序發起 I/O 操作後繼續執行其他任務，不會被 I/O 操作阻塞。
2. 高並發： AIO 適用於高並發的網絡應用，因為它可以使用較少的線程處理大量的連接。
3. 事件驅動： AIO 采用事件驅動的方式，應用程序需要註冊回調函數來處理非同步事件，這種模型適用於事件驅動的應用，如高性能的網絡伺服器。
4. 適應瞬時高負載： AIO 適合處理瞬時高負載的情況，因為它可以有效地處理大量的並發連接，而不會導致線程資源耗盡。

總結：AIO 是 Java 中的一種非同步 I/O 模型，適用於高並發、事件驅動的網絡應用，它通過非同步操作和回調機制提高了 I/O 操作的效率和可伸縮性。


### 其他參考文獻

http://www.52im.net/thread-4283-1-1.html

## Netty

Netty 是一個高性能、異步事件驅動的網路應用框架，基於 Java 的 NIO（New I/O）技術。它旨在簡化網路應用程序的開發，提供了強大的抽象和易用的 API，同時具有出色的性能和可擴展性。

*  Netty特點

1. 異步和事件驅動：Netty 使用異步和事件驅動的模型，使得開發者可以編寫非阻塞的、高效率的網路應用。

2. 高性能：Netty 的性能出色，具有低延遲和高吞吐量。它是許多高並發網路應用的首選框架。
 
3. 模塊化和可擴展：Netty 是一個模塊化的框架，你可以根據需求選擇性地使用它的模塊。它還支持自定義的編解碼器和處理器，以滿足特定需求。
 
4. 多協議支援：Netty 提供對多種協議的支援，包括 HTTP、WebSocket、SSL/TLS、Google Protobuf、zlib/gzip 壓縮等。
 
5. 零拷貝：Netty 支持零拷貝技術，可以提高數據傳輸的效率，減少數據複製的開銷。
 
6. 安全性：Netty 支持安全的網路通信，包括 SSL/TLS 加密。


![](https://hackmd.io/_uploads/HyAQciH03.png)

**綠色的部分**Core核心模塊，包括零拷貝、API庫、可擴展的事件模型。

**橙色的部分**Protocol Support協議支持，包括Http協議、webSocket、SSL(安全套接字協議)、谷歌Protobuf協議、zlib/gzip壓縮與解壓縮、Large File Transfer大文件傳輸等等。

**紅色的部分**Transport Services傳輸服務，包括Socket、Datagram、Http Tunnel等等。



## Java IO模型的演進
1. BIO（Blocking I/O）： 初始版本，最早的Java版本使用，JDK 1.0 到 JDK 1.3 主要提供了同步阻塞的 I/O 模型，也就是 BIO（Blocking I/O）。在BIO模型中，每個I/O操作都是阻塞的，這意味著當執行I/O操作時，執行緒會被阻塞，直到I/O操作完成。這導致了伺服器的可擴展性問題，因為每個連接需要一個獨立的執行緒。

2. NIO（Non-blocking I/O）： 隨著Java 1.4的引入，NIO被引入以解決BIO的可擴展性問題。NIO引入了通道（Channel）和緩衝區（Buffer）的概念，並提供了選擇器（Selector）來監聽多個通道上的事件。這使得一個單線程可以管理多個連接，提高了伺服器的性能和擴展性。
 
3. AIO（Asynchronous I/O）： AIO是Java 7的一部分，主要用於處理非常大的並發連接。AIO引入了異步I/O操作的概念，它們不會阻塞執行緒，因此一個執行緒可以同時處理多個I/O操作。這對高性能伺服器應用程序非常有用，例如高度並發的網絡服務。


## BIO

    
    BIO程式設計簡單流程
伺服器端啟動一個ServerSocket；

使用者端啟動Socket對伺服器進行通 信，預設情況下伺服器端需要對每 個客戶 建立一個執行緒與之通訊；

使用者端發出請求後, 先諮詢伺服器 是否有執行緒響應，如果沒有則會等 待，或者被拒絕；

如果有響應，使用者端執行緒會等待請 求結束後，在繼續執行；
    
    1.等待連線

    2.取得連線, 分派該連線給其他Thread處理

    3.回頭繼續等待連線
    
    BIO （Blocking I/O）：有一排水壺在燒開水，BIO的工作模式就是，叫一個線程停留在一個水壺那，直到這個水壺燒開，才去處理下一個水壺。但是實際上線程在等待水壺燒開的時間段什麼都沒有做。

原文網址：https://kknews.cc/comic/j5mpo8q.html