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

### Reactor Model
## 同步、非同步、阻塞、非阻塞
Synchronous (同步)、Asynchronous (非同步)
在這裡意義為在執行IO動作時, 是由jvm處理或是交由OS處理來區分. 若為jvm處理則為Synchronous, 否則為Asynchronous.
Block(阻塞)、Non-Block(非阻塞)
依據"進行IO操作時是否需要等待"作為區分. 若操作IO時當下的Thread需要等待則為Block, 否則為Non-Block.


## BIO

## 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而偷懶.

海底撈很好喫,但是經常要排隊。我們就以生活中的這個例子進行講解。
A 顧客去喫海底撈,就這樣乾坐着等了一小時,然後纔開始喫火鍋。(BIO)
B 顧客去喫海底撈,他一看要等挺久,於是去逛商場,每次逛一會就跑回來看有沒有排到他。於是他最後既購了物,又喫上海底撈了。(NIO)
C 顧客去喫海底撈,由於他是高級會員,所以店長說,你去商場隨便玩吧,等下有位置,我立馬打電話給你。於是 C 顧客不用幹坐着等,也不用每過一會兒就跑回來看有沒有等到,最後也喫上了海底撈(AIO)
哪種方式更有效率呢?是不是一目瞭然呢?
## AIO

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 加密。

**綠色的部分**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