Netty與NIO超詳細講解
Linux下的五種I/O模型
1)阻塞I/O(blocking I/O)
2)非阻塞I/O (nonblocking I/O)
3) I/O復用(select 和poll) (I/O multiplexing)
4)信號驅動I/O (signal driven I/O (SIGIO))
5)異步I/O (asynchronous I/O (the POSIX aio_functions))
前面四種都是同步io、第五種是異步IO;
阻塞式:當程序沒有獲取到數(shù)據(jù)的時候,整個應用可能會產生阻塞,放棄了CPU執(zhí)行,無法去做其他事情。
非阻塞式:不管有沒有獲取到數(shù)據(jù),都必須立馬返回一個結果,如果沒有獲取到數(shù)據(jù)的情況下返回一個錯誤標記,根據(jù)錯誤的標記不斷輪詢。BIO屬于阻塞式的io操作??梢允褂枚嗑€程實現(xiàn)異步I0,同時處理多個請求。缺點:非常消耗服務器資源CPU
阻塞IO的流程
BIO屬于阻塞式的io操作。
可以使用多線程實現(xiàn)異步IO,同時處理多個請求。缺點:非常消耗服務器資源CPU

當我們在調用一個io函數(shù)的時候,如果沒有獲取到數(shù)據(jù)的情況下,那么就會一直等待;等待的過程中會導致整個應用程序一直是一個阻塞的過程,無法去做其他的實現(xiàn)。
IO復用
IO復用機制:IO實際指的就是網(wǎng)絡的IO、多路也就是多個不同的tcp連接;復用也就是指使用同一個線程合并處理多個不同的IO操作,這樣的話可以減少CPU資源。
信號驅動I/O
發(fā)出一個請求實現(xiàn)觀察監(jiān)聽,當有數(shù)據(jù)的時候直接走我們異步回調;
異步IO
異步io也就是發(fā)出請求數(shù)據(jù)之后,剩下的事情完全實現(xiàn)異步完成
同步與異步
站在多線程的角度總結:
同步整個應用代碼執(zhí)行順序是從上往下執(zhí)行 并且返回到結果;
異步:開啟多個不同的分支實現(xiàn)并行執(zhí)行 每個線程互不影響;
站在web項目角度分析
默認的情況Http請求就是一個同步形式實現(xiàn)調用 基于請求與響應,如果我們響應非常耗時的話,會導致客戶端一直等待(用戶體驗非常不好)
NIO
Java的nio是在Jdk1.4版本之后推出了一套新的io方案,這種io方案對原有io做了一次性能上的升級
NIO翻譯成英文 no blocking io 簡稱為 nio 非阻塞io,不是new io。
比傳統(tǒng)的io支持了面向緩沖區(qū)、基于通道實現(xiàn)的io的方案。
BIO 與 NIO 區(qū)別:
Bio是一個阻塞式的io,它是西向與流傳輸也就是根據(jù)每個字節(jié)實現(xiàn)傳輸效率非常低,
而我們的nio是雨向與緩沖區(qū)的非阻塞邊.其中最大的亮點:運多路復用機制,
I0多路復用
多路實際上指的是多個不同的 tcp 連接。
復用:一個線程可以維護多個不同的io操作。
優(yōu)點:占用cpu資源非常小、保證線程安全問題。
IO 多路復用實現(xiàn)原理
使用一個Java案例來描述IO多路復用的思路:
public class SocketNioTcpServer {
private static List<SocketChannel> listSocketChannel = new ArrayList<>();
private static ByteBuffer byteBuffer = ByteBuffer.allocate(512);
public static void main(String[] args) {
try {
// 1.創(chuàng)建一個ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 綁定地址
ServerSocketChannel bind = serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
socketChannel.configureBlocking(false);
listSocketChannel.add(socketChannel);
}
for (SocketChannel scl : listSocketChannel) {
try {
int read = scl.read(byteBuffer);
if (read > 0) {
byteBuffer.flip();
Charset charset = Charset.forName("UTF-8");
String receiveText = charset.newDecoder().decode
(byteBuffer.asReadOnlyBuffer()).toString();
System.out.println("receiveText:" + receiveText);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}NIO核心組件
通道(Channel)
通常我們nio所有的操作都是通過通道開始的,所有的通道都會注冊到統(tǒng)一個選擇器(Selector)上實現(xiàn)管理,在通過選擇器將數(shù)據(jù)統(tǒng)一寫入到 buffer中。
緩沖區(qū)(Buffer)
Buffer本質上就是一塊內存區(qū),可以用來讀取數(shù)據(jù),也就先將數(shù)據(jù)寫入到緩沖區(qū)中、在統(tǒng)一的寫入到硬盤上。
選擇器(Selector)
Selector可以稱做為選擇器,也可以把它叫做多路復用器,可以在單線程的情況下可以去維護多個Channel,也可以去維護多個連接;
使用Java原生API實現(xiàn)NIO操作
public class NIOServer {
/**
* 創(chuàng)建一個選擇器
*/
private Selector selector;
public void initServer(int port) throws IOException {
// 獲得一個ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 設置通道為非阻塞
serverSocketChannel.configureBlocking(false);
// 將該通道對應的ServerSocket綁定到port端口
serverSocketChannel.bind(new InetSocketAddress(port));
// 獲得一個通道管理器
this.selector = Selector.open();
// 將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_ACCEPT事件,注冊該事件后,
// 當該事件到達時,selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞。
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws IOException {
System.out.println("服務端啟動成功!");
// 輪詢訪問selector
while (true) {
// 當注冊的事件到達時,方法返回;否則,該方法會一直阻塞
int select = selector.select();
if (select == 0) {
continue;
}
// 獲得selector中選中的項的迭代器,選中的項為注冊的事件
Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 刪除已選的key,以防重復處理
ite.remove();
if (key.isAcceptable()) {// 客戶端請求連接事件
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 獲得和客戶端連接的通道
SocketChannel channel = server.accept();
// 設置成非阻塞
channel.configureBlocking(false);
// 在和客戶端連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限。
channel.register(this.selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {// 獲得了可讀的事件
read(key);
}
}
}
}
public void read(SelectionKey key) throws IOException {
// 服務器可讀取消息:得到事件發(fā)生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 創(chuàng)建讀取的緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(512);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("服務端收到信息:" + msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
channel.write(outBuffer);// 將消息回送給客戶端
}
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(8000);
server.listen();
}
}Redis為什么支持高并發(fā)
Redis官方沒有windows版本redis,只有l(wèi)inux版本的reids。
Redis的底層是采用nio 多路io復用機制實現(xiàn)對多個不同的連接(tcp)實現(xiàn)io的復用;能夠非常好的支持高并發(fā),同時能夠先天性支持線程安全的問題。為什么現(xiàn)場安全?因為使用一個線程維護多個不同的io操作 原理使用nio的選擇器,將多個不同的Channel統(tǒng)一交給我們的selector(選擇器管理)。
但是nio的實現(xiàn)在不同的操作系統(tǒng)上存在差別:在我們windows操作系統(tǒng)上使用 select 實現(xiàn)輪訓機制、在linux操作系統(tǒng)使用epoll
備注:windows操作系統(tǒng)是沒有epoll
在windows操作系統(tǒng)中使用select實現(xiàn)輪訓機制時間復雜度是為 o(n),而且這種情況也會存在空輪訓的情況,效率非常低、其次默認對我們的輪訓有一定限制,所以這樣的話很難支持上萬tcp連接。
所以在這時候linux操作就出現(xiàn)epoll實現(xiàn)事件驅動回調形式通知,不會存在空輪訓的情況,只是對活躍的socket實現(xiàn)主動回調,這樣的性能有很大的提升 所以時間復雜度為是o(1)
注意:windows操作系統(tǒng)沒有epoll、只有l(wèi)inux操作系統(tǒng)有。
所以為什么Nginx、redis能夠支持非常高的并發(fā) 最終都是靠的linux版本的 io 多路復用機制epoll
到此這篇關于Netty與NIO超詳細講解的文章就介紹到這了,更多相關Netty NIO內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java Mybatis框架Dao層的實現(xiàn)與映射文件以及核心配置文件詳解分析
MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數(shù)和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO為數(shù)據(jù)庫中的記錄2021-10-10
java 使用ImageIO.writer從BufferedImage生成jpeg圖像遇到問題總結及解決
這篇文章主要介紹了java 使用ImageIO.writer從BufferedImage生成jpeg圖像遇到問題總結及解決的相關資料,需要的朋友可以參考下2017-03-03
SpringBoot中使用Zookeeper實現(xiàn)分布式鎖的案例
本文主要介紹了SpringBoot中使用Zookeeper實現(xiàn)分布式鎖的案例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-01-01
在Struts2中如何將父類屬性序列化為JSON格式的解決方法
本篇文章,小編將為大家介紹關于在Struts2中如何將父類屬性序列化為JSON格式的解決方法,有需要的朋友可以參考一下2013-04-04
Spring Data JPA帶條件分頁查詢實現(xiàn)原理
這篇文章主要介紹了Spring Data JPA帶條件分頁查詢實現(xiàn)原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-05-05

