摘要:使用來優化套接字操作,盡可能消除由的緩沖區實現所導致的性能以及內存使用率的懲罰,這種優化發生在的核心代碼中,不會被暴露出來。當前將會被增加所寫入的字節數。
ByteBuf是Java NIO ByteBuffer的替代品,是網絡數據基本單位字節的容器。
ByteBuf的APINetty的數據處理API通過兩個組件暴漏:抽象類ByteBuf和接口ByteBufHolder
ByteBuf優點:
他可以被用戶自定義的緩沖區類型擴展
通過內置的復合緩沖區類型實現了透明的零拷貝
容量可以按需增長
在讀寫兩種模式之間切換不需要調用ByteBuffer的flip()方法
讀寫使用不同的索引
支持方法的鏈式調用
支持引用計數
支持池化
ByteBuf——數據容器 工作原理?ByteBuf維護兩個索引:一個用于讀取,一個用于寫入。當你從ByteBuf讀取時,readIndex會被遞增已經被讀取的字節數,同樣的,當向ByteBuf中寫入數據時,writeIndex也會被遞增。
如果readIndex和writeIndex處于同樣的位置,再次嘗試讀取數據將會觸發IndexOutOfBoundsException
名稱以read或者write開頭的ByteBuf方法,將會推進其對應的索引,而名稱以set或者get開頭的操作則不會。
名稱以set或者get開頭的方法會有一個索引位置參數,將會在該索引位置上進行set或get操作
ByteBuf可以指定最大容量。如果寫索引超過這個值會觸發異常IllegalArgumentException。默認的最大值是Integer.MAX_VALUE
ByteBuf使用模式 堆緩沖區?將數據存儲在JVM的對空間中,這種模式又被成為支撐數組。它能在沒有使用池化的情況下提供快速的分配和釋放。
當hasArray方法返回false時,嘗試訪問支撐數組將觸發UnsupportedOperationException異常。
直接緩沖區?NIO引入的ByteBuffer類允許JVM實現通過本地調用來分配內存,這樣可以避免在每次調用本地I/O操作之前(或者之后)將緩沖區的內容復制到一個中間緩沖區(或者從中間緩沖區把內容復制到緩沖區)。
?直接緩沖區的主要特點是,分配和釋放都比較昂貴。
復合緩沖區?它為多個ByteBuf提供了一個聚合視圖。可以根據需要添加或者刪除ByteBuf實例。Netty通過CompositeByteBuf(ByteBuf的子類)實現這個模式,它提供了一個將多個緩沖區表示為單個合并緩沖區的虛擬表示。
CompositeByteBuf中的ByteBuf實例可能同時包含直接內存分配和非直接內存分配。如果只有一個ByteBuf實例,那么CompositeByteBuf上的hasArray方法將返回該ByteBuf上的hasArray方法的值,否則將返回false。
?CompositeByteBuf不支持訪問支撐數組,因此訪問CompositeByteBuf中的數據類似于訪問直接緩沖區的模式。
?Netty使用CompositeByteBuf來優化套接字I/O操作,盡可能消除由JDK的緩沖區實現所導致的性能以及內存使用率的懲罰,這種優化發生在Netty的核心代碼中,不會被暴露出來。
字節級操作 隨機訪問索引?ByteBuf的索引是從0開始:第一個字節的索引就是0,最后一個字節的索引是capacity() - 1。
如果方法中有一個索引值參數,通過該方法訪問數據既不會改變readIndex也不會改變writeIndex。
如果有需要,可以通過調用readIndex(index)或者writeIndex(index)手動移動兩者。
順序訪問索引ByteBuf內部分段示意圖如下:
可丟棄字節?在上圖中可丟棄字節指的就是已被讀取過的字節,通過調用discardReadBytes()方法,可以丟棄它們并回收空間。可丟棄字節分段的初始大小為0,即readIndex,該值會隨著read操作的執行而增加(get*操作不會移動readIndex)。
discardReadBytes()方法只是移動了可以讀取的字節以及writeIndex,而沒有對所有可寫入的字節進行擦除寫。
discardReadBytes()會導致內存復制,因為可讀字節必須要移動到緩沖區開始的位置。
可讀字節?可讀字節分段存儲了實際數據。新分配的、包裝的或者復制的緩沖區的默認readINdex值為0。任何名稱以read或者skip開頭的操作都將檢索或者跳過位于當前readIndex的數據,并將它增加已讀字節數。
如果被調用的方法需要一個ByteBuf參數作為寫入的目標,并且沒有指定目標索引參數,那么該寫入的目標的writeIndex也將被增加。
可寫字節?可以字節分段是指一個擁有未定義內容的、寫入就緒的內存區域。新分配的緩沖區的writeIndex的默認值為0。任何以write開頭的方法都將從當前的writeIndex開始寫數據,并將它增加已經寫入的字節數。如果寫操作的目標也是ByteBuf,并且沒有指定源索引的值,則源緩沖區的readerIndex也會被增加相同的大小。
索引管理? JDK的InputStream定義了mark(int readLimit)和reset()方法,這些方法分別被用來將流中的當前位置標記為指定的值,以及將流重置到該位置。
?在ByteBuf中,可以調用markReadIndex()、markWriteIndex()、resetReaderIndex()、resetWriterIndex()來標記和重置ByteBuf的readIndex和writeIndex,不過在ByteBuf中沒有readLimit參數指定標記啥時候失效。
?在ByteBuf中,也可以通過調用readerIndex(int)或者writeIndex(int)來將索引移動到指定位置。
?在ByteBuf中,可以通過clear()方法將readerIndex和writeIndex都設置為0,但是不會清楚內存中的內容。調用clear()方法比調用discardReadBytes()更加輕量,因為clear只是重置索引,不會復制任何的內存。
查找操作?最簡單的確定值的索引的方法是indexOf()。較復雜的查找可以通過一個ByteProcessor(Netty4.1版本以上,舊的版本采用ByteBufProcessor)參數達成。
派生緩沖區?派生緩沖區為ByteBuf提供了以專門的方式來呈現其內容的視圖,這類視圖的創建方式主要有以下幾種:
duplicate()
slice()
slice(int, int)
Unpooled.unmodifiableBuffer(...)
order(ByteOrder)
readSlice(int)
以上方法都會返回一個新的ByteBuf實例,它具有自己的讀索引、寫索引和標記索引。它會和源實例共享內存,因此創建成本低廉,但是如果修改它的內容,也就意味著修改了對應的源實例。
如果需要一個現有緩沖區的真實副本,需要使用copy()或者copy(int,int)方法
讀/寫操作讀寫操作主要分為兩類:
get()和set()操作,從給定的索引開始,并且保持索引不變
read()和write()操作,從給定的索引開始,并且會根據已經訪問過的字節數對索引進行調整。
方法名稱 | 描述 |
---|---|
getBoolean(int) | 返回給定索引處的Boolean值 |
getByte(int) | 返回給定索引處的字節 |
getUnsignedByte(int) | 將給定索引處的無符號字節值作為short返回 |
getMedium(int) | 返回給定索引處的24位的中等int值 |
getUnsignedMedium(int) | 返回給定索引處的無符號的24位的中等int值 |
getInt(int) | 返回給定索引處的int值 |
getUnsignedInt(int) | 將給定索引處的無符號int值作為long返回 |
getLong(int) | 返回給定索引處long值 |
getShort(int) | 返回給定索引處的short值 |
getUnsignedShort(int) | 將給定索引處的無符號short值作為int返回 |
getByte(int, ...) | 將該緩沖區中從給定索引開始的數據傳送到指定的目的地 |
方法名稱 | 描述 |
---|---|
setBoolean(int, boolean) | 設定給定索引處的Boolean值 |
setByte(int index, int value) | 設定給定索引處的字節值 |
setMedium(int index, int value) | 設定給定索引處的24位的中等int值 |
setInt(int index, int value) | 設定給定索引處的int值 |
setLong(int index, long value) | 設定給定索引處的long值 |
setShort(int index, int value) | 設定給定索引處的short值 |
方法名稱 | 描述 |
---|---|
readBoolean() | 返回當前readIndex處的Boolean值,并將readIndex增加1 |
readByte() | 返回當前readIndex處的字節,并將readIndex增加1 |
readUnsignedByte() | 將當前readIndex處的無符號字節值作為short返回,并將readIndex增加1 |
readMedium() | 返回當前readIndex處的24位的中等int值,并將readIndex增加3 |
readUnsignedMedium() | 返回當前readIndex處的24位的無符號的中等int值,并將readIndex增加3 |
readInt() | 返回當前readIndex處的int值,并將readIndex增加4 |
readUnsignedMedium() | 返回當前readIndex處的24位的無符號的中等int值,并將readerIndex增加3 |
readInt() | 返回當前readIndex處的int值,并將readerIndex增加4 |
readUnsignedInt() | 將當前readerIndex處的無符號的int值作為long值返回,并將readIndex增加4 |
readLong() | 返回當前readIndex處的long值,并將readIndex增加8 |
readShort() | 返回當前readIndex處的short值,并將readIndex增加2 |
readUnsignedShort() | 將當前readIndex處的無符號short值作為int值返回,并將readIndex增加2 |
readBytes(ByteBuf byte[] destination, int dstIndex, [, int length]) | 將當前ByteBuf中從當前readIndex處開始的(如果設置了,length長度的字節)數據傳送到一個目標ByteBuf或者byte[],從目標的dstIndex開始的位置。本地的readIndex將被增加已經傳輸的字節數。 |
方法 | 描述 |
---|---|
writeBoolean(boolean) | 在當前writeIndex處寫入一個boolean值,并將writeIndex增加1 |
writByte(byte) | 在當前writeIndex處寫入一個字節值,并將writeIndex增加1 |
writeMedium(int) | 在當前writeIndex處寫入一個中等的int值,并將writeIndex增加3 |
writeInt(int) | 在當前writeIndex處寫入一個int值,并將writeIndex增加4 |
writeLong(long) | 在當前writeIndex處寫入一個long值,并將writeIndex增加8 |
writeShort(int) | 在當前writeIndex處寫入一個short值,并將writeIndex增加2 |
writeBytes(source ByteBuf byte[] [,int srcIndex,int length]) | 從當前writeIndex開始,傳輸來自于指定源(ByteBuf或者byte[])的數據。如果提供了srcIndex和length,則從srcIndex開始讀取,并且處理長度為length的字節。當前writeIndex將會被增加所寫入的字節數。 |
方法 | 描述 |
---|---|
isReadable() | 如果至少有一個字節可供讀取,則返回true |
isWritable() | 如果至少有一個字節可被寫入,則返回true |
readableBytes() | 返回可被讀取的字節數 |
writableBytes() | 返回可被寫入的字節數 |
capacity() | 返回ByteBuf可容納的字節數。在此之后,它會嘗試再次擴展直到達到maxCapacity() |
maxCapacity() | 返回ByteBuf可以容納的最大字節數 |
hasArray() | 如果ByteBuf由一個字節數組支撐,則返回true |
array() | 如果ByteBuf由一個字節數組支撐則返回該數組;否則,它將拋出一個UnsupportedOperationException異常 |
?ByteBufHolder為Netty的高級特性提供了支持,如緩沖區池化,其中可以從池中借用ByteBuf,并且在需要時自動釋放。
名稱 | 描述 |
---|---|
content() | 返回由這個ByteBufHolder所持有的ByteBuf |
copy() | 返回這個ByteBufHolder的一個深拷貝,包括一個其所包含的ByteBuf的非共享拷貝 |
duplicate() | 返回這個ByteBufHolder的一個淺拷貝,包括一個其所包含的ByteBuf的共享拷貝 |
?為了降低分配和釋放內存的開銷,Netty通過ByteBufAllactor接口實現了(ByteBuf的)池化,它可以用來分配我們所描述過的任意類型的ByteBuf實例。
名稱 | 描述 |
---|---|
buffer() buffer(int initialCapacity) buffer(int initialCapacity, int maxCapacity) |
返回一個基于堆或者直接內存存儲的ByteBuf |
heapBuffer() heapBuffer(int initialCapacity) heapBuffer(int initialCapacity, int maxCapacity) |
返回一個基于堆內存存儲的ByteBuf |
directBuffer() directBuffer(int initialCapacity) directBuffer(int initialCapacity, int maxCapacity) |
返回一個基于直接內存存儲的ByteBuf |
compositeBuffer() compositeBuffer(int maxNumComponents) compositeDirectBuffer() compositeDirectBuffer(int maxNumComponents) compositeHeapBuffer() compositeHeapBuffer(int maxNumComponents) |
返回一個可以通過添加最大到指定數目的基于堆的或者直接內存存儲的緩沖區來擴展的CompositeByteBuf |
ioBuffer() | 返回一個用于套接字的I/O操作的ByteBuf |
Netty提供了兩種ByteBufAllocator的實現:PooledByteBufAllocator和ByteBufAllocator。前者池化了ByteBuf的實例以提高性能并最大限度的減少內存碎片,后者的實現不池化ByteBuf實例,并且每次被調用時都會返回一個新的實例。
Netty4.1以后默認使用PooledByteBufAllocator
Unpooled緩沖區?Netty提供了一個簡單的成為Unpooled的工具類,它提供了靜態的輔助方法來創建未池化的ByteBuf實例。
方法 | 描述 |
---|---|
buffer() buffer(int initialCapacity) buffer(int initialCapacity, int maxCapacity) |
返回一個未池化的基于堆內存存儲的ByteBuf |
directBuffer() directBuffer(int initialCapacity) directBuffer(int initialCapacity, int maxCapacity) |
返回一個未池化的基于直接內存存儲的ByteBUf |
wrappedBuffer() | 返回了一個包裝了給定數據的ByteBuf |
copiedBuffer() | 返回了一個復制了給定數據的ByteBuf |
?ByteBufUtil提供了用于操作ByteBuf的靜態的輔助方法。
方法 | 描述 |
---|---|
hexdump() | 以十六進制的表示形式打印ByteBuf的內容 |
equals(ByteBuf, ByteBuf) | 用來判斷兩個ByteBuf實例的相等性 |
?引用計數是一種通過在某個對象所持有的資源不再被其他對象引用時釋放該對象所持有的資源來優化內存使用和性能技術。
Netty為ByteBuf和ByteBufHolder引入了引用計數技術,它們實現了ReferenceCounted接口。
試圖訪問一個已經被釋放的引用計數的對象,將會導致一個IllegalReferenceCountException
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69454.html
摘要:目前為止,我們已經完成了一半的工作,剩下的就是在方法中啟動服務器。第一個通常被稱為,負責接收已到達的。這兩個指針恰好標記著數據的起始終止位置。 前言 本篇翻譯自netty官方Get Start教程,一方面能把好的文章分享給各位,另一方面能鞏固所學的知識。若有錯誤和遺漏,歡迎各位指出。 https://netty.io/wiki/user-gu... 面臨的問題 我們一般使用專用軟件或者...
時間:2018年04月11日星期三 說明:本文部分內容均來自慕課網。@慕課網:https://www.imooc.com 教學源碼:https://github.com/zccodere/s... 學習源碼:https://github.com/zccodere/s... 第一章:課程介紹 1-1 課程介紹 什么是Netty 高性能、事件驅動、異步非阻塞的IO Java開源框架 基于NIO的客戶...
摘要:服務器構成至少一個該組件實現了服務器對從客戶端接受的數據的處理,即它的業務邏輯引導配置服務器的啟動代碼。至少,它會將服務器綁定到它要監聽連接請求的端口上。需要注意的是,由服務器發送的消息可能會被分塊接受。 Netty服務器構成 至少一個ChannelHandler——該組件實現了服務器對從客戶端接受的數據的處理,即它的業務邏輯 引導——配置服務器的啟動代碼。至少,它會將服務器綁定...
摘要:事件循環新連接接入連接上的數據讀取抽象連接抽象業務邏輯處理讀寫數據期間的業務層動態鏈處理多個組成,讓消息可以層層處理數據接收基本的數據處理基于公眾號貓說學習交流群現架構設計碼農兼創業技術顧問,不羈平庸,熱愛開源,雜談程序人生與不定期干貨。 本博客 貓叔的博客,轉載請申明出處閱讀本文約 4分鐘 適讀人群:同學 Java IO,Socket非阻塞通信流程 這里我們使用一個內嵌的永久循環,...
摘要:轉發自 轉發自 http://netty.io/wiki/referenc... Since Netty version 4, the life cycle of certain objects are managed by their reference counts, so that Netty can return them (or their shared resources)...
閱讀 793·2021-11-11 16:54
閱讀 1531·2021-08-24 10:01
閱讀 1916·2019-08-30 15:54
閱讀 3302·2019-08-29 14:02
閱讀 3137·2019-08-28 18:22
閱讀 2251·2019-08-28 18:09
閱讀 3712·2019-08-26 10:26
閱讀 2674·2019-08-23 18:23