java nio 對消息分片_Java NIO:應用

Java NIO 是一種非阻塞的、面向塊而非字節的 IO 方式。雖然 Java 的傳統 IO 也進行了一些基于 NIO 的改造,NIO 仍然能夠帶來許多優勢。

面向流的 IO 方便我們一個字節一個字節地處理數據,有利于實現過濾等功能,更加優雅和簡單。相應地,其速度通常比較慢。

Java NIO 的模型由三部分組成。Channel 通道,類似于傳統 IO 中的流,用來實際傳輸數據。

Buffer 緩沖,我們用來讀取和發送數據的位置。

Selector 選擇器,可以在一個線程上綁定多個 Channel 和對應的 Buffer 。

Channel

Channel 和流非常相似。區別是,通道支持異步讀寫,支持雙向讀寫,而且基于緩沖區。相比之下,流通常是單向同步讀寫的。

常用的 Channel 主要包括:FileChannel 文件

DatagramChannel UDP數據報

SocketChannel TCP 套接字

ServerSocketChannel TCP 服務端套接字

Buffer

Java 中的各種基本類型都有其對應的 Buffer ,最常用的是 ByteBuffer 。可以通過 Channel 或者手動寫入數據。

然后,要從 buffer 中讀取數據,需要首先 flip() 它,變成讀取模式。

需要注意的是,很多 Channel ,如 FileChannel 和非阻塞模式下的 SocketChannel 的 write 方法并不能保證將 buffer 全部寫入文件。因此,要使用循環來處理:

下面是一個簡單的輸出文件內容的示例:

InputStream 和 OutputStream 類也有類似的 getChannel 方法。當然,這樣開啟的通道只能是單向的。下面是一個將輸入內容傳輸到輸出的可復用的代碼片段:

Scatter / Gather

Scatter 和 Gather 可以譯為分散和聚集,指的是向同一個通道寫入和讀出多個 Buffer 的過程。在處理復雜結構的數據,如 Header-Content 時,有利于代碼整潔。Scatter Read 指從一個 Channel 讀取到多個 Buffer ,Gather Write 指從多個 Buffer 寫入到一個 Channel 。關于網絡的內容還會在后面進一步解釋。

網絡和異步 IO

異步 IO 的模式實際上來自于操作系統,如 Linux 的 IO 復用和 Windows 的 IOCP 。因此,類似的編程模式很可能也適用于其他語言。

TCP 異步 IO 的例子

異步 IO 不會阻塞,這也使得它可以處理許多的 IO 連接。在傳統 IO 下,這通常需要通過輪詢或多線程來實現。

首先回顧一下普通的 IO 編程中處理 TCP 連接的方法:ServerSocket 類監聽端口,客戶端的 Socket 類構造時發出連接請求。這時,ServerSocket.accept() 從阻塞中脫離,返回服務端的 Socket 對象。

然后,我們來看異步處理的方法:

這里的 SelectionKey.OP_ACCEPT 是適用于 ServerSocketChannel 的唯一事件,即 TCP 連接建立的事件。

select() 方法會阻塞直到有任何一個連接建立。selectedKeys 返回一個 Set 對象。在異步 IO 的類似處理過程中,由于我們已經通過 select 得到了這個連接信息,就不必再擔心 accept 會阻塞:

可以看到,我們將 accept 得到的結果重新放回了 selector 的監聽列表,并且將監聽事件修改為了 SelectionKey.OP_READ ,即有數據到達的事件。這個過程和傳統 IO 中從 ServerSocket.accept() 獲得 Socket 的過程類似。

然后,在 if 語句的另一個分支,我們來處理接收數據的過程。使用 channel() 方法得到雙向讀寫的通道對象。隨后,我們就可以使用之前熟悉的 buffer 來處理這個連接了。

最后,我們需要把處理結束的連接從 keys 中刪除,以免重復處理。在實際的應用場景中,我們還需要把關閉的連接從 Selector 中去除,并且很有可能使用多線程。

SelectionKey

上面我們見到了 OP_READ 和 OP_ACCEPT 。除此之外,NIO 還有 OP_WRITE 和 OP_CONNECT 兩種事件。可以認為每個事件代表“就緒”:例如當連接另一方傳來數據時,連接處于“讀就緒”狀態,對應事件 OP_READ 。因此,寫就緒和連接就緒這兩種事件并不常用。

四種事件的值分別為 1、2、8、16,因此可以使用位操作來處理這些事件。例如:

相應地,SelecttionKey 也提供了一些處理這些信息的方法。

還可以為每個 SectionKey 附加一個對象,以方便識別類似的對象。

Selector

除了 select() 方法外,Selector 類同樣提供了帶有超時的阻塞方法和非阻塞,允許返回 0 的方法。

如果在阻塞期間調用 Selector 的 wakeUp() 方法(當然,是在另一個線程里),線程會立刻放棄阻塞。在操作結束之后,需要使用 Selector.close() 方法。這將會使所有的 SelectionKey 都無效,但并不會關閉 Channel 。

異步 IO 設計

概述

阻塞 IO 和異步 IO 的區別是顯而易見的。阻塞 IO 是一種成功的設計,它能夠保證 IO 的可靠和簡單。但在這種模式下,每個 IO 都需要單獨的一個線程來處理。在 JVM 的默認參數下,32 bit 系統的一個棧為 320kB ,64 bit 下更達到了 1MB ,在高并發情況下這是完全無法接受的。線程池是解決這個問題的一種途徑,但當我們面臨大量低速長鏈接的時候,問題仍然沒有被徹底解決。而這正是大規模互聯網應用的常態。因此,異步 IO 成為了必然的選擇。異步 IO 的最典型特征是,每一次檢查不再是阻塞或獲得整塊數據,而是0或數據。這雖然解決了多線程的問題,卻帶來了另外一些需要解決的問題。

異步 IO 首先要解決的問題是,怎樣用一個線程處理許多連接。于是,我們有了 Selector ,使用一個 Selector 來處理許多連接,以實現“阻塞直到有一個”的效果,而不需要去處理那些尚未讀到數據的連接。于是,線程資源被充分地利用起來。

第二個問題是,由于所有的操作都被立即返回,異步 IO 下讀到的數據不總是完整的。甚至,在連續傳輸的情況下,幾乎總是不完整的。于是,我們需要做兩件事:判斷當前的數據是否是完整的

將不完整的數據暫存起來,以備下一次傳輸時拼合起來。

于是,我們在 Selector 與 Channel 之間加入一個 Message Reader,用來處理這些工作。在工程化的實踐中,我們可能希望這套系統能夠處理各種不同的協議。因此,可能會接收一個 Message Reader 的工廠作為參數,以進行依賴注入。

Message Reader 的實現

前面我們看到,Message Reader 需要能夠在內部的一個 Buffer 中存儲不完整的 Message 。顯而易見,這個 buffer 的大小應該等于消息的最大值。但這時我們又遇到了之前說的內存不足的問題:百萬級別的 1 MB buffer 意味著 1 TB 的 RAM 空間。因此,我們需要在這里使用可伸縮的(flexible)buffer 。

拷貝擴容

一種常見的方法是熟悉的拷貝擴容,也就是當 buffer 已滿后將所有內容復制到一個更大的數組中去。在這種方式下, threshold 的 選取就是一個重要的問題。例如,假設一個系統的請求消息不大于 4 kB ,傳輸的文件通常不大于 128 kB ,更大的文件則沒有規律性。那么,我們就會將 threshold 設置為 4kB 和 128 kB ,將最終的內存占用控制在 GB 級別。

追加擴容

另一種常見的方式是追加(append)擴容,方法是用一個列表將所有小的 buffer 片段集合起來,或者將一個大的數組分片,再用列表來管理分片。后者在內存模型上會更有利一些,但需要對并發量的準確判斷。追加擴容的缺點也很明顯,維護和讀取都比較復雜。

使用 TLV 消息

許多協議,包括 HTTP/2 在內,開始使用 TLV 格式的消息。TLV 指的是 Type-Length-Value 的元組。對于這類消息,我們可以在一開始就知道消息的長度,并為其開辟好內存空間,避免了上面的方式中對內存資源的浪費。

當然,TLV 格式也有其缺點。對于很長的 TLV 消息,我們就需要很大的內存空間的預開辟,這也為 DoS 攻擊提供了空間。一種解決方案是使用分段 TLV 的消息格式,但這并不能徹底解決問題。另一種方式是為消息設置超時時間。這樣,服務器至少能夠在一段時間的無響應后恢復。

寫不完整的消息

前面已經提到,非阻塞模式的通道并不能對一次 write() 實際寫入的數據量做出保證,而是將寫入的數據的字節數返回給調用者。于是,為了進一步解耦和提高效率,我們還需要在數據處理者和 Channel 同樣準備一個 Message Writer ,用來處理這個不穩定的輸出過程。

回過頭來想,我們在這里并不想為每個連接都維護一個線程。因此,我們只希望對有消息可寫的 Writer 進行處理。因此,我們使用這樣一個過程:

當 Message Writer 有消息可寫時,才將其對應的 Channel 注冊到 Selector 。然后,服務器在空閑時檢查 Selector 來獲取可寫的 Channel ,并尋找其對應的 Writer 以寫入數據。在 Writer 已經沒有數據可寫時,將 Channel 從 Selector 上解綁。

集成

現在我們已經理清了輸入和輸出兩個部分,現在我們從整個服務器的角度來思考。總的來說,一個服務器會執行這樣一個循環:

從 ServerSocket 中獲取 Socket => Select 讀事件 => 將接受的數據交給 Reader 來處理 => 在核心部分處理 Reader 傳來的完整數據 => 將處理后的數據交給 Writer => Select 寫事件

當然,這些功能還可以在多個線程內完成。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537982.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537982.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537982.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用Notepad++來比較文件

原文連接:https://blog.csdn.net/sanqima/article/details/50467154 -----------------------------------------------------------------------------Notepad,也叫npp,是一款類似于記事本的開源小軟件。它運行便攜,體積小、資源…

利用lamda函數 的函數式編程 實現scrapy審計外網安全問題

2019獨角獸企業重金招聘Python工程師標準>>> import scrapy from scrapy.spiders import Spiderdef parse_response(response, ip_in):title response.xpath("/html/head/title").extract()[0]filename quotes.txtwith open(filename, a) as f:f.write(…

Linux服務器上新增開放端口號

原文連接: https://blog.csdn.net/u012562106/article/details/52882666 ---------------------------------------開放端口的方法: 方法一:命令行方式 1. 開放端口命令: /sbin/iptables -I INPUT -p tcp --dport 8…

return ,continue,break的用法與區別總結

1、return 語句的作用 (1) return 從當前的方法中退出,返回到該調用的方法的語句處,繼續執行。 (2) return 返回一個值給調用該方法的語句,返回值的數據類型必須與方法的聲明中的返回值的類型一致,可以使用強制類型轉換來是數據類型一致。 (…

linux java -xms_為什么JVM比指定的-Xms消耗更少的內存?

我的問題是 Headers ,我通過搜索獲得了一些知識:但還是不知道為什么,有些可以提供一些幫助嗎?這是我在Ubuntu12.04(64位)上的測試運行JDK 1.7.0_04 . 和頂部顯示如下:PID USER PR NI VIRT RES SHR S %CPU %MEM TIME CO…

Jquery的一些方法

$.trim(str);說明:去掉字符串首尾空格。轉載于:https://www.cnblogs.com/gygtech/p/9552538.html

清除nginx服務器網站緩存數據

原文連接:https://blog.csdn.net/Crabime/article/details/51883028?locationNum2&fps1 -------------------------------------公司網站上線之后后面老是出現一些小的bug,但是這些bug在vi上改了之后打開更改的網頁卻發現并沒有更改,goo…

linux安裝指定版本python_ubuntu多版本python為指定版本python安裝庫

當安裝了多個版本的Python時,比如一個python2.7,一個python3.5,需要為某個python版本安裝庫時如何精準的指定python版本呢。網上多數回答都是用到pyenv、virtualenv之類,看了下感覺跟我的預期不一致。因為PyCharm已經可以管理多個…

Linux 如何通過某一臺服務器調用執行多臺遠程服務器上的腳本,結果顯示在本地?...

現在都流行自動化運維了,可能目前技術不夠,很多自動化工具還不怎么會用,所以本次只是通過ssh來實現功能。 說明:自己寫的一個簡單腳本,只是實現了基礎功能,還有待優化。 一共三臺機器: master&a…

linux下查看磁盤分區的文件系統格式

原文鏈接: https://www.cnblogs.com/youbiyoufang/p/7607174.html ------------------------------------------------------------------------- df -T 只可以查看已經掛載的分區和文件系統類型。 Filesystem Type 1K-blocks Used Available Use% Mounted on/dev/s…

ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)

Linux下mysql的安裝,真是不同Linux環境,各種問題。步驟: 1、暫停mysql服務 service mysqld stop2、設置mysql模式# mysqld_safe --usermysql --skip-grant-tables --skip-networking & 3、clone session,新窗口# mysql -u roo…

mysql 跳表 b 樹_簡單談談Mysql索引與redis跳表

摘要面試時,交流有關mysql索引問題時,發現有些人能夠濤濤不絕的說出B樹和B樹,平衡二叉樹的區別,卻說不出B樹和hash索引的區別。這種一看就知道是死記硬背,沒有理解索引的本質。本文旨在剖析這背后的原理,歡…

(Ajax)axios源碼簡析(三)——請求與取消請求

傳送門: axios源碼簡析(一)——axios入口文件axios源碼簡析(二)——Axios類與攔截器axios源碼簡析(三)——請求與取消請求請求過程 在Axios.prototype.request中我們看到,要先通過請…

Windows配置tomcat環境

1、安裝JDK 參考教程: https://jingyan.baidu.com/article/6dad5075d1dc40a123e36ea3.htmlCLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jarCLASSPATH這個環境變量一定要配好,否則tomcat起不來,直接復制上面的內容,…

java 抽獎 高并發處理_如何設計高并發下的抽獎?

關于抽獎,需要考慮的點有很多,這里稍微整理了下主要需要考慮以下三點:用戶抽獎次數限制獎品數量限制獎品發放的分布中獎的概率的可控性用戶抽象次數限制一個用戶必須限制抽獎的次數,而同一個用戶的并發幾率其實是很小的,所以這里可以用悲觀鎖來控制用戶的抽獎次數。獎品數量限制…

WPF圓角按鈕與觸發顏色變化

原文:WPF圓角按鈕與觸發顏色變化<Button x:Name"button1" Content"按鈕1" Margin"10,10,0,0" Cursor"Pen"><Button.Template><ControlTemplate><Border CornerRadius"15,15,15,15"><Border.Back…

咖啡豆的勵志故事

好多年前就聽過這個故事&#xff0c;以前沒感觸&#xff0c;最近特有感觸。

java bean spring_JavaBean和Spring bean傻傻分不清楚

JavaBean的定義可序列化提供無參構造提供getter/setter方法疑問在學習 Spring 的過程中發現很多 bean 對象并沒有實現 Serializable 接口或提供其他可序列化的操作。這種也叫 bean&#xff1f;或者 bean 也可以不提供序列化操作&#xff1f;解決stackoverflow 一番后&#xff0…

WPF Image Source 設置相對路徑圖片

原文:WPF Image Source 設置相對路徑圖片BitmapImage bt new BitmapImage(new Uri("Images\\3_u10484.png", UriKind.Relative));this.Img1.Source bt;

PowerDesigner V16.5 安裝教程以及漢化(數據庫建模)

原文地址&#xff1a;https://blog.csdn.net/tgbyn/article/details/72809116 ----------------------------------------------------------------------一、power designer是什么以及是干什么的&#xff1f; power designer是能進行數據庫設計的強大的軟件&#xff0c;是一款…