自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從I/O多路復(fù)用到Netty,還要跨過(guò)Java NIO包

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
Netty實(shí)際上也是一個(gè)封裝好的框架,它的網(wǎng)絡(luò)I/O本質(zhì)上還是使用了Java的NIO包(New IO,不是網(wǎng)絡(luò)I/O模型的NIO,Nonblocking IO)包。所以,從網(wǎng)絡(luò)I/O模型到Netty,我們還需要了解下Java NIO包。

[[389262]]

上一篇文章我們深入了解了I/O多路復(fù)用的三種實(shí)現(xiàn)形式,select/poll/epoll。

那Netty是使用哪種實(shí)現(xiàn)的I/O多路復(fù)用呢?這個(gè)問(wèn)題,得從Java NIO包說(shuō)起。

Netty實(shí)際上也是一個(gè)封裝好的框架,它的網(wǎng)絡(luò)I/O本質(zhì)上還是使用了Java的NIO包(New IO,不是網(wǎng)絡(luò)I/O模型的NIO,Nonblocking IO)包。所以,從網(wǎng)絡(luò)I/O模型到Netty,我們還需要了解下Java NIO包。

本文預(yù)計(jì)閱讀時(shí)間 5 分鐘,將重點(diǎn)回答以下幾個(gè)問(wèn)題:

  • 如何用Java NIO包實(shí)現(xiàn)一個(gè)服務(wù)端
  • Java NIO包如何實(shí)現(xiàn)I/O多路復(fù)用模型
  • 有了Java NIO包,為什么還要封裝一個(gè)Netty?

1.先來(lái)看一個(gè)Java NIO服務(wù)端的例子

上一篇文章我們已經(jīng)了解了I/O多路復(fù)用的實(shí)現(xiàn)形式。

就是多個(gè)的進(jìn)程的IO可以注冊(cè)到一個(gè)復(fù)用器(selector)上,然后用一個(gè)進(jìn)程調(diào)用select,select會(huì)監(jiān)聽(tīng)所有注冊(cè)進(jìn)來(lái)的IO。

NIO包做了對(duì)應(yīng)的實(shí)現(xiàn)。如下圖所示。


有一個(gè)統(tǒng)一的selector負(fù)責(zé)監(jiān)聽(tīng)所有的Channel。這些channel中只要有一個(gè)有IO動(dòng)作,就可以通過(guò)Selector.select()方法檢測(cè)到,并且使用selectedKeys得到這些有IO的channel,然后對(duì)它們調(diào)用相應(yīng)的IO操作。

我們來(lái)個(gè)簡(jiǎn)單的demo做一下演示。如何使用NIO中三個(gè)核心組件(Buffer緩沖區(qū)、Channel通道、Selector選擇器)來(lái)編寫(xiě)一個(gè)服務(wù)端程序。

  1. public class NioDemo { 
  2.     public static void main(String[] args) { 
  3.         try { 
  4.             //1.創(chuàng)建channel 
  5.             ServerSocketChannel socketChannel1 = ServerSocketChannel.open(); 
  6.             //設(shè)置為非阻塞模式,默認(rèn)是阻塞的 
  7.             socketChannel1.configureBlocking(false); 
  8.             socketChannel1.socket().bind(new InetSocketAddress("127.0.0.1", 8811)); 
  9.  
  10.             ServerSocketChannel socketChannel2 = ServerSocketChannel.open(); 
  11.             socketChannel2.configureBlocking(false); 
  12.             socketChannel2.socket().bind(new InetSocketAddress("127.0.0.1", 8822)); 
  13.  
  14.             //2.創(chuàng)建selector,并將channel1和channel2進(jìn)行注冊(cè)。 
  15.             Selector selector = Selector.open(); 
  16.             socketChannel1.register(selector, SelectionKey.OP_ACCEPT); 
  17.             socketChannel2.register(selector, SelectionKey.OP_ACCEPT); 
  18.  
  19.             while (true) { 
  20.                 //3.一直阻塞直到有至少有一個(gè)通道準(zhǔn)備就緒 
  21.                 int readChannelCount = selector.select(); 
  22.                 Set<SelectionKey> selectionKeys = selector.selectedKeys(); 
  23.                 Iterator<SelectionKey> iterator = selectionKeys.iterator(); 
  24.                 //4.輪訓(xùn)已經(jīng)就緒的通道 
  25.                 while (iterator.hasNext()) { 
  26.                     SelectionKey key = iterator.next(); 
  27.                     iterator.remove(); 
  28.                     //5.判斷準(zhǔn)備就緒的事件類(lèi)型,并作相應(yīng)處理 
  29.                     if (key.isAcceptable()) { 
  30.                         // 創(chuàng)建新的連接,并且把連接注冊(cè)到selector上,并且聲明這個(gè)channel只對(duì)讀操作感興趣。 
  31.                         ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel(); 
  32.                         SocketChannel socketChannel = serverSocketChannel.accept(); 
  33.                         socketChannel.configureBlocking(false); 
  34.                         socketChannel.register(selector, SelectionKey.OP_READ); 
  35.                     } 
  36.                     if (key.isReadable()) { 
  37.                         SocketChannel socketChannel = (SocketChannel) key.channel(); 
  38.                         ByteBuffer readBuff = ByteBuffer.allocate(1024); 
  39.                         socketChannel.read(readBuff); 
  40.                         readBuff.flip(); 
  41.                         System.out.println("received : " + new String(readBuff.array())); 
  42.                         socketChannel.close(); 
  43.                     } 
  44.                 } 
  45.             } 
  46.         } catch (IOException e) { 
  47.             e.printStackTrace(); 
  48.         } 
  49.     } 

通過(guò)這個(gè)代碼示例,我們能清楚地了解如何用Java NIO包實(shí)現(xiàn)一個(gè)服務(wù)端:

  • 1)創(chuàng)建channel1和channel2,分別監(jiān)聽(tīng)特定端口。
  • 2)創(chuàng)建selector,并將channel1和channel2進(jìn)行注冊(cè)。
  • 3)selector.select()一直阻塞,直到有至少有一個(gè)通道準(zhǔn)備就緒。
  • 4)輪訓(xùn)已經(jīng)就緒的通道
  • 5)并根據(jù)事件類(lèi)型做出相應(yīng)的響應(yīng)動(dòng)作。程序啟動(dòng)后,會(huì)一直阻塞在selector.select()。

通過(guò)瀏覽器調(diào)用localhost:8811 或者 localhost:8822就能觸發(fā)我們的服務(wù)端代碼了。

2.Java NIO包如何實(shí)現(xiàn)I/O多路復(fù)用模型

上文演示的Java NIO服務(wù)端已經(jīng)比較清楚地展示了使用NIO編寫(xiě)服務(wù)端程序的過(guò)程。

那這個(gè)過(guò)程中如何實(shí)現(xiàn)了I/O多路復(fù)用的呢?

我們得深入看下selector的實(shí)現(xiàn)。

  1. //2.創(chuàng)建selector,并將channel1和channel2進(jìn)行注冊(cè)。 
  2. Selector selector = Selector.open(); 

從open這里開(kāi)始吧。


這里用了一個(gè)SelectorProvider來(lái)創(chuàng)建selector。

進(jìn)入SelectorProvider.provider(),看到具體的provider是由

sun.nio.ch.DefaultSelectorProvider創(chuàng)建的,對(duì)應(yīng)的方法是:


咦?原來(lái)不同的操作系統(tǒng)會(huì)提供不同的provider對(duì)象。這里包括了PollSelectorProvider、EPollSelectorProvide等。

名字是不是有點(diǎn)眼熟?

沒(méi)錯(cuò),跟我們上一篇文章分析過(guò)的I/O多路復(fù)用的不同實(shí)現(xiàn)方式poll/epoll有關(guān)。

我們選擇默認(rèn)的

sun.nio.ch.PollSelectorProvider往下看看。


OK,找到了實(shí)現(xiàn)類(lèi)PollSelectorImpl。

然后,通過(guò)以下調(diào)用:


找到最終的native方法poll0。


是不是仍然很眼熟?

沒(méi)錯(cuò)!跟我們上一篇文章分析過(guò)的poll函數(shù)是一致的。

  1. int poll (struct pollfd *fds, unsigned int nfds, int timeout); 

繞了這么久,到最后,還是找到了我們聊過(guò)I/O多路復(fù)用的 poll 實(shí)現(xiàn)。

至此,我們終于把Java NIO和 I/O多路復(fù)用模型串聯(lián)起來(lái)了。

Java NIO包使用selector,實(shí)現(xiàn)了I/O多路復(fù)用模型。

同時(shí),在不同的操作系統(tǒng)中,會(huì)有不同的poll/epoll選擇。

3.為什么還需要Netty呢?

那既然已經(jīng)有了NIO包了,我們可以自己手動(dòng)編寫(xiě)服務(wù)框架了,為什么還需要封裝一個(gè)Netty框架呢?有什么好處呢?

好處當(dāng)然是有很多了!我們從一開(kāi)始實(shí)現(xiàn)的demo說(shuō)起。

3.1 設(shè)計(jì)模式的優(yōu)化

我們的demo確實(shí)已經(jīng)能夠工作了,但是還是有比較明顯的問(wèn)題。第4步(輪詢已經(jīng)就緒的通道)和第5步(對(duì)事件作相應(yīng)處理)是在同一個(gè)線程中的,當(dāng)事件處理比較耗時(shí)甚至阻塞時(shí),整個(gè)流程就會(huì)阻塞了。

我們使用的實(shí)際上就是 “單Reactor單線程” 設(shè)計(jì)模式。


這種模型在Reactor中負(fù)責(zé)監(jiān)聽(tīng)端口、接收請(qǐng)求,如果是連接事件交給acceptor處理,如果是讀寫(xiě)事件和業(yè)務(wù)處理就交給handler處理,但始終只有一個(gè)線程執(zhí)行所有的事情。

為了提高性能,我們理所當(dāng)然相當(dāng)可以把事件處理交給線程池,那就可以演進(jìn)為 “單Reactor多線程” 設(shè)計(jì)模式。


這種模型和第一種模型的主要區(qū)別是把業(yè)務(wù)處理從之前的單一線程脫離出來(lái),換成線程池處理。Reactor線程只處理連接事件、讀寫(xiě)事件,所有業(yè)務(wù)處理都交給線程池,充分利用多核機(jī)器的資源,提高性能。

但是這仍然不夠!

我們可以發(fā)現(xiàn),一個(gè)Reactor線程承擔(dān)了所有的網(wǎng)絡(luò)事件,例如監(jiān)聽(tīng)和響應(yīng),高并發(fā)場(chǎng)景下單線程存在性能問(wèn)題。

為了充分利用多核能力,可以構(gòu)建兩個(gè) Reactor,主 Reactor 單獨(dú)監(jiān)聽(tīng)server socket,accept新連接,然后將建立的 SocketChannel 注冊(cè)給指定的從 Reactor,從Reactor再執(zhí)行事件的讀寫(xiě)、分發(fā),把業(yè)務(wù)處理就扔給worker線程池完成。這就演進(jìn)為 ”主從Reactor模式“ 設(shè)計(jì)模式。


所以,如果有人直接幫我們 封裝好這樣的設(shè)計(jì)模式 ,是不是太好了?

沒(méi)錯(cuò),Netty就是這樣的“活雷鋒”!

Netty就使用了主從Reactor模式封裝了Java NIO包的使用,大大提高了性能。

3.2 其他優(yōu)點(diǎn) (以后的核心知識(shí)點(diǎn))

除了封裝了高性能的設(shè)計(jì)模式外,Netty還有許多其他優(yōu)點(diǎn):

穩(wěn)定性。 Netty 更加可靠穩(wěn)定,修復(fù)和完善了 JDK NIO 較多已知問(wèn)題,包括 select 空轉(zhuǎn)導(dǎo)致 CPU 消耗 100%、keep-alive 檢測(cè)等問(wèn)題。

性能優(yōu)化。對(duì)象池復(fù)用技術(shù)。Netty 通過(guò)復(fù)用對(duì)象,避免頻繁創(chuàng)建和銷(xiāo)毀帶來(lái)的開(kāi)銷(xiāo)。零拷貝技術(shù)。 除了操作系統(tǒng)級(jí)別的零拷貝技術(shù)外,Netty 提供了面向用戶態(tài)的零拷貝技術(shù),在 I/O 讀寫(xiě)時(shí)直接使用 DirectBuffer,避免了數(shù)據(jù)在堆內(nèi)存和堆外內(nèi)存之間的拷貝。

便捷性。 Netty 提供了很多常用的工具,例如行解碼器、長(zhǎng)度域解碼器等。如果我們使用JDK NIO包,那么這些常用工具都需要自己進(jìn)行實(shí)現(xiàn)。

正是因?yàn)?Netty 做到了高性能、高穩(wěn)定性、高易用性,完美彌補(bǔ)了 Java NIO 的不足,所以在我們?cè)诰W(wǎng)絡(luò)編程時(shí),首選Netty,而不是自己直接使用Java NIO。

回顧一下前幾章內(nèi)容,到目前為止,我們從網(wǎng)絡(luò)I/O模型出發(fā),一步步了解到了Netty的網(wǎng)絡(luò)I/O模型。

對(duì)于I/O多路復(fù)用、Java NIO包 和 Netty 的關(guān)系也有了全面的認(rèn)識(shí)。

有了這些知識(shí)基礎(chǔ),我們初步了解了Netty是什么,為什么使用Netty。

后面的文章,我們將逐步展開(kāi)Netty框架的核心知識(shí)點(diǎn),敬請(qǐng)期待。

 

責(zé)任編輯:姜華 來(lái)源: 阿丸筆記
相關(guān)推薦

2021-02-10 08:09:48

Netty網(wǎng)絡(luò)多路復(fù)用

2023-05-08 00:06:45

Go語(yǔ)言機(jī)制

2011-12-08 10:51:25

JavaNIO

2021-03-17 16:53:51

IO多路

2023-08-07 08:52:03

Java多路復(fù)用機(jī)制

2020-10-13 07:51:03

五種IO模型

2023-11-08 09:22:14

I/ORedis阻塞

2024-12-30 00:00:05

2021-06-09 19:25:13

IODubbo

2022-12-08 09:10:11

I/O模型Java

2019-12-23 14:53:26

IO復(fù)用

2022-09-12 06:33:15

Select多路復(fù)用

2022-04-13 07:59:23

IOBIONIO

2023-01-09 10:04:47

IO多路復(fù)用模型

2020-08-31 07:16:04

BIONIO多路復(fù)用器

2020-10-14 09:11:44

IO 多路復(fù)用實(shí)現(xiàn)機(jī)

2021-05-31 06:50:47

SelectPoll系統(tǒng)

2009-06-29 18:09:12

多路復(fù)用Oracle

2021-03-04 08:34:55

同步阻塞非阻塞

2024-08-08 14:57:32

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)