成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

徹底搞懂 Netty 線程模型

開發 前端
在學習Netty 之前我們最好先掌握 BIO、NIO、AIO 基礎知識,前面我們已經花了三篇文章去講這些知識。我們開始來學習 Netty 的具體知識了,本文就Netty線程模型展開分析。

 前言

  • BIO 、NIO 、AIO 總結
  • Unix網絡編程中的五種IO模型
  • 深入理解IO多路復用實現機制

在學習Netty 之前我們最好先掌握 BIO、NIO、AIO 基礎知識,前面我們已經花了三篇文章去講這些知識。我們開始來學習 Netty 的具體知識了,本文就Netty線程模型展開分析。

基本概念
IO 模型

  • BIO:同步阻塞模型;
  • NIO:基于IO多路復用技術的“非阻塞同步”IO模型。簡單來說,內核將可讀可寫事件通知應用,由應用主動發起讀寫事件;
  • AIO:非阻塞異步IO模型。簡單來說,內核將讀完成事件通知應用,讀操作由內核完成,應用只需要操作數據即可;應用做異步寫操作時立即返回,內核會進行寫操作排隊并執行寫操作。

NIO 和 AIO 不同之處在于應用是否進行真正的讀寫操作。

reactor 和 proactor 模型

  • reactor:基于NIO技術,可讀可寫時通知應用;
  • proactor:基于AIO技術,讀完成時通知應用,寫操作應用通知內核。

Netty認識
Netty是Java領域有名的開源網絡庫,特點是高性能和高擴展性,因此很多流行的框架都是基于它來構建的,比如我們熟知的Dubbo、Rocketmq、Hadoop等。

通過前面 NIO 的學習可以看到,NIO 的類庫和API 繁雜,例如 Selector、 ServerSocketChannel、 SocketChannel、 ByteBuffer等這些對于從事應用層的程序員來說,使用起來開發工作量和難度都非常大。另外客戶端面臨斷連重連、 網絡閃斷、心跳處理、半包讀寫、 網絡擁塞 和異常流的處理等等。

Netty 對 JDK 自帶的 NIO 的 API 進行了良好的封裝,解決了上述問題。且Netty擁有高性能、 吞吐量更高,延遲更低,減少資源消耗,最小化不必要的內存復制等優點。

Netty 現在都在用的是4.x,5.x版本已經廢棄,Netty 4.x 需要JDK 6以上版本支持。

在了解Netty使用場景后,本節將從IO模型的演進角度來分析Netty線程模型,通過并發編程之父Doug Lea所寫《Scalable IO in Java》中涉及的一些IO處理模式,一步一步深入理解Netty線程模型的“進化歷史”。

《Scalable IO in Java》:

http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

Netty使用場景

  1. 互聯網行業:在分布式系統中,各個節點之間需要遠程服務調用,高性能的 RPC 框架必不可少,Netty 作為異步高性能的通信框架,往往作為基礎通信組件被這些 RPC 框架使用。典型的應用有:阿里分布式服務框架 Dubbo 的 RPC 使用 Dubbo 協議進行通信,Dubbo 協議默認使用 Netty 作為基礎通信組件,用于實現各進程節點之間的內部通信;消息中間件Rocketmq底層也是用的Netty作為基礎通信組件。
  2. 游戲行業:無論是手游服務端還是大型的網絡游戲,Java 語言都得到了越來越廣泛的應用。Netty 作為高性能的基礎通信組件,它本身提供了 TCP/UDP 和 HTTP 協議棧。
  3. 大數據領域:經典的 Hadoop 的高性能通信和序列化組件 Avro 的 RPC 框架,默認采用 Netty 進行跨節點通信,它的 Netty Service 是基于 Netty 框架二次封裝實現。

Netty相關開源項目:

https://netty.io/wiki/related-projects.html

IO處理模式演進
基本上所有的網絡處理程序都遵循以下基本的處理(handler)流程:

  1. Read request (接收二進制數據)
  2. Decode request (解碼為可讀數據)
  3. Process service (對數據進行處理產生結果)
  4. Encode reply (將結果編碼為二進制數據)
  5. Send reply (返回結果)

傳統網絡服務器會為每一個連接的處理開啟一個新的線程,即我們前面所說的BIO模型(多線程模式),我們可以看下大致的示意圖:

上圖為 BIO版本

BIO模型對于每一個請求都分發給一個線程(可以理解為一個handler),每個handler中都獨自處理上面1-5流程。這種模型的適用場景和瓶頸可以查看《BIO 、NIO 、AIO 總結》。

改進:采用基于事件驅動的設計,當有事件觸發時,才會調用處理器進行數據處理(非阻塞)。這就是對應的NIO線程模型。

上圖為單線程版事件驅動模型,可以理解為NIO單線程版本

上面用到了 Reactor 模式。關于 Reactor 模式的兩個概念:

  • Reactor:負責響應 IO 事件,當檢測到一個新的事件,將其發送給相應的 Handler 去處理。
  • Handler:負責處理非阻塞的行為,標識系統管理的資源,同時將 Handler 與事件綁定。

注意:Reactor 為單個線程,如上圖所示:不僅需要處理客戶端的 accept 連接請求,同時也要負責分發(dispatch)讀寫請求到處理器中。由于只有單個線程處理各種請求,所以要求處理器中的業務需要能夠快速處理完。

改進:現在的服務器基本上是多核 CPU,那么在多處理器場景下,為實現服務的高性能我們可以有目的的采用多線程模式處理業務。

上圖為多線程版本事件驅動模型,可以理解為NIO多線程版本

通過與NIO單線程模型相比,增加了worker線程池,專門用于處理非IO操作(decode、compute、encode),大大提高了工作效率。

這種模型下,客戶端發送過來的連接和注冊還是由主線程 Reactor 統一去處理,只不過客戶端連接成功后的后續事件分發給 worker 線程池去處理。

但是,當客戶端短時間內幾十萬或者上百萬條連接請求的時候(雙十一、春運搶票),單個 Rector 不僅要處理注冊事件,也要同時分發任務到 worker 線程池,由于分發也是比較耗時的操作,有可能會導致阻塞。

繼續改進:將Reactor 拆分為兩部分

上圖為主從NIO模型

如上圖所示,在這種模型下,mainReactor 專門負責新客戶端的連接操作,建立通道(channel),然后將一定事件內的 channel 注冊到另外一個 subReactor,subReactor 負責將客戶端的讀寫請求交給線程池處理。這樣即使幾十萬個請求同時到來也無所謂了。

通俗理解,mainReactor就是大總管,只負責接口,subReactor就是一個員工,負責給總管接待客戶提供服務。

Netty線程模型
通過對 《 Scalable IO in Java 》里的一些 IO 處理模式理解, Netty的線程模型就是由上面主從NIO模型演變來的,是基于Reactor模型的。

如下圖所示,Boos Group 就是上面提到的 mainReactor,與上面不同在于 Worker Group,它可以理解為一組 subReactor,即在大總管下面有多個員工來干活,每次接收的客戶都均勻分配給不同員工。Netty 之所以單機支持百萬級別并發量,就是因為一主多從的線程模型。

需要說明的是,Netty 的線程模型并不是一成不變的。它通常采用一主多從,但是也可以根據實際需要配置啟動參數,通過設置不同的啟動參數,Netty 可以同時支持 “多主多從”。

下面是對上圖“一主多從” Netty 模型的詳細解釋:

  1. Netty 抽象除兩組線程池 BossGroup 和 WorkerGroup,BossGroup 專門負責接收客戶端的連接,WorkerGroup 專門負責網絡的讀寫。
  2. BossGroup 和 WorkerGroup 類型都是 NioEventLoopGroup。
  3. NioEventLoopGroup 相當于一個事件循環線程組,這個組中含有多個事件循環線程,每一個事件循環線程是 NioEventLoop。
  4. 每個 NioEventLoop 都有一個 selector,用于監聽注冊在其上的 socketChannel 的網絡通訊。
  5. 每個 Boss NioEventLoop 線程內部循環執行的步驟有 3 步:處理accept事件,與 client 建立連接,生成 NioSocketChannel;將NioSocketChannel 注冊到某個 worker NioEventLoop 上的 selector;處理任務隊列的任務,即runAllTasks。
  6. 每個 worker NIOEvent'Loop線程循環執行的步驟:輪詢注冊到最近的 selector 上所有的 NioSocketChannel 的 read、write 事件;處理 I/O 事件,即 read、write事件,在對應的NioScoketChannel 處理業務;runAllTask 處理任務隊列 TaskQueue 的任務,一些耗時的業務處理一般可以放入 TaskQueue 中,這樣不影響數據在 pipeline 中的流動處理。
  7. 每個 worker NIOEventLoop 處理 NioSocketChannel 業務時,會使用 pipeline (管道),管道中維護來很多 handler 處理器用來處理 channel 中的數據。

Netty 模塊組件
Bootstrap、ServerBootstrap
Bootstrap 意思是引導,一個 Netty 應用通常由一個 Bootstrap 開始,主要作用是配置整個 Netty程序,通過鏈式調用串聯各個組件。Netty 中 Bootstrap 類是客戶端程序的啟動引導類,ServerBootstrap 是服務端 啟動引導類。

Future、ChannelFuture
正如前面介紹,在 Netty 中所有的 IO 操作都是異步的,不能立刻得知消息是否被正確處理。但是可以過一會等它執行完成或者直接注冊一個監聽,具體的實現就是通過 Future 和 ChannelFutures,他們可以注冊一個監聽,當操作執行成功或失敗時監聽會自動觸發注冊的監聽事件。

Channel
Netty 網絡通信的組件,能夠用于執行網絡 I/O 操作。Channel 為用戶提供:

  1. 當前網絡連接的通道的狀態(例如是否打開?是否已連接?)
  2. 網絡連接的配置參數 (例如接收緩沖區大小)
  3. 提供異步的網絡 I/O 操作(如建立連接,讀寫,綁定端口),異步調用意味著任何 I/O 調用都將立即返回,并且不保證在調用結束時所請求的 I/O 操作已完成。
  4. 調用立即返回一個 ChannelFuture 實例,通過注冊監聽器到 ChannelFuture 上,可以 I/O 操作成功、失敗或取消時回調通知調用方。
  5. 支持關聯 I/O 操作與對應的處理程序。不同協議、不同的阻塞類型的連接都有不同的Channel 類型與之對應。下面是一些常用的 Channel 類型:
  1. NioSocketChannel,異步的客戶端 TCP Socket 連接。(最常用) 
  2. NioServerSocketChannel,異步的服務器端 TCP Socket 連接 
  3. NioDatagramChannel,異步的 UDP 連接 
  4. NioSctpChannel,異步的客戶端 Sctp 連接 
  5. NioSctpServerChannel,異步的 Sctp 服務器端連接,這些通道涵蓋了 UDP 和 TCP 網絡 IO 以及文件 IO。 

Selector
Netty 基于 Selector 對象實現 I/O 多路復用,通過 Selector 一個線程可以監聽多個連接的 Channel 事件。當向一個 Selector 中注冊 Channel 后,Selector 內部的機制就可以自動不斷地查詢(Select) 這些注冊的 Channel 是否有已就緒的 I/O 事件(例如可讀,可寫,網絡連接完成等),這樣程序就可以很簡單地使用一個線程高效地管理多個 Channel 。

NioEventLoop
NioEventLoop 中維護了一個線程和任務隊列,支持異步提交執行任務,線程啟動時會調用 NioEventLoop 的 run 方法,執行 I/O 任務和非 I/O 任務:

  1. I/O 任務即 selectionKey 中 ready 的事件,如 accept、connect、read、write 等,由 processSelectedKeys 方法觸發。
  2. 非 IO 任務,添加到 taskQueue 中的任務,如 register0、bind0 等任務,由 runAllTasks 方法觸發。

NioEventLoopGroup
NioEventLoopGroup,主要管理 eventLoop 的生命周期,可以理解為一個線程池,內部維護了一組線程,每個線程(NioEventLoop)負責處理多個 Channel 上的事件,而一個 Channel 只對應于一個線程。

ChannelHandler
ChannelHandler 是一個接口,處理 I/O 事件或攔截 I/O 操作,并將其轉發到其 ChannelPipeline(業務處理鏈)中的下一個處理程序。ChannelHandler 本身并沒有提供很多方法,因為這個接口有許多的方法需要實現,方便使用期間,可以繼承它的子類:

  1. ChannelInboundHandler 用于處理入站 I/O 事件 
  2. ChannelOutboundHandler 用于處理出站 I/O 操作 

或者使用以下適配器類:

  1. ChannelInboundHandlerAdapter 用于處理入站 I/O 事件。 
  2. ChannelOutboundHandlerAdapter 用于處理出站 I/O 操作。 

ChannelHandlerContext
保存 Channel 相關的所有上下文信息,同時關聯一個 ChannelHandler 對象。

ChannelPipline
保存 ChannelHandler 的 List,用于處理或攔截 Channel 的入站事件和出站操作。ChannelPipeline 實現了一種高級形式的攔截過濾器模式,使用戶可以完全控制事件的處理方式,以及 Channel 中各個的 ChannelHandler 如何相互交互。在 Netty 中每個 Channel 都有且僅有一個 ChannelPipeline 與之對應,它們的組成關系如下:

一個 Channel 包含了一個 ChannelPipeline,而 ChannelPipeline 中又維護了一個由 ChannelHandlerContext 組成的雙向鏈表,并且每個 ChannelHandlerContext 中又關聯著一個 ChannelHandler。read事件(入站事件)和write事件(出站事件)在一個雙向鏈表中,入站事件會從鏈表 head 往后傳遞到最后一個入站的 handler,出站事件會從鏈表 tail 往前傳遞到最前一個出站的 handler,兩種類型的 handler 互不干擾。

Netty通訊示例
Netty的maven依賴

  1. <dependencies> 
  2.  <dependency> 
  3.   <groupId>io.netty</groupId> 
  4.         <artifactId>netty-all</artifactId> 
  5.         <version>4.1.52.Final</version> 
  6.     </dependency> 
  7. </dependencies> 

服務端代碼

  1. package com.niuh.netty.base; 
  2.  
  3. import io.netty.bootstrap.ServerBootstrap; 
  4. import io.netty.channel.ChannelFuture; 
  5. import io.netty.channel.ChannelInitializer; 
  6. import io.netty.channel.ChannelOption; 
  7. import io.netty.channel.EventLoopGroup; 
  8. import io.netty.channel.nio.NioEventLoopGroup; 
  9. import io.netty.channel.socket.SocketChannel; 
  10. import io.netty.channel.socket.nio.NioServerSocketChannel; 
  11.  
  12. public class NettyServer { 
  13.  
  14.     public static void main(String[] args) throws Exception { 
  15.         //創建兩個線程組bossGroup和workerGroup, 含有的子線程NioEventLoop的個數默認為cpu核數的兩倍 
  16.         // bossGroup只是處理連接請求 ,真正的和客戶端業務處理,會交給workerGroup完成 
  17.         EventLoopGroup bossGroup = new NioEventLoopGroup(1); 
  18.         EventLoopGroup workerGroup = new NioEventLoopGroup(); 
  19.         try { 
  20.             //創建服務器端的啟動對象 
  21.             ServerBootstrap bootstrap = new ServerBootstrap(); 
  22.             //使用鏈式編程來配置參數 
  23.             bootstrap.group(bossGroup, workerGroup) //設置兩個線程組 
  24.                     .channel(NioServerSocketChannel.class) //使用NioServerSocketChannel作為服務器的通道實現 
  25.                     // 初始化服務器連接隊列大小,服務端處理客戶端連接請求是順序處理的,所以同一時間只能處理一個客戶端連接。 
  26.                     // 多個客戶端同時來的時候,服務端將不能處理的客戶端連接請求放在隊列中等待處理 
  27.                     .option(ChannelOption.SO_BACKLOG, 1024) 
  28.                     .childHandler(new ChannelInitializer<SocketChannel>() {//創建通道初始化對象,設置初始化參數 
  29.  
  30.                         @Override 
  31.                         protected void initChannel(SocketChannel ch) throws Exception { 
  32.                             //對workerGroup的SocketChannel設置處理器 
  33.                             ch.pipeline().addLast(new NettyServerHandler()); 
  34.                         } 
  35.                     }); 
  36.             System.out.println("netty server start。。"); 
  37.             //綁定一個端口并且同步, 生成了一個ChannelFuture異步對象,通過isDone()等方法可以判斷異步事件的執行情況 
  38.             //啟動服務器(并綁定端口),bind是異步操作,sync方法是等待異步操作執行完畢 
  39.             ChannelFuture cf = bootstrap.bind(9000).sync(); 
  40.             //給cf注冊監聽器,監聽我們關心的事件 
  41.             /*cf.addListener(new ChannelFutureListener() { 
  42.                 @Override 
  43.                 public void operationComplete(ChannelFuture future) throws Exception { 
  44.                     if (cf.isSuccess()) { 
  45.                         System.out.println("監聽端口9000成功"); 
  46.                     } else { 
  47.                         System.out.println("監聽端口9000失敗"); 
  48.                     } 
  49.                 } 
  50.             });*/ 
  51.             //對通道關閉進行監聽,closeFuture是異步操作,監聽通道關閉 
  52.             // 通過sync方法同步等待通道關閉處理完畢,這里會阻塞等待通道關閉完成 
  53.             cf.channel().closeFuture().sync(); 
  54.         } finally { 
  55.             bossGroup.shutdownGracefully(); 
  56.             workerGroup.shutdownGracefully(); 
  57.         } 
  58.     } 

服務端所注冊的自定義回調函數 NettyServerHandler:

  1. package com.niuh.netty.base; 
  2.  
  3. import io.netty.buffer.ByteBuf; 
  4. import io.netty.buffer.Unpooled; 
  5. import io.netty.channel.ChannelHandlerContext; 
  6. import io.netty.channel.ChannelInboundHandlerAdapter; 
  7. import io.netty.util.CharsetUtil; 
  8.  
  9. /** 
  10.  * 自定義Handler需要繼承netty規定好的某個HandlerAdapter(規范) 
  11.  */ 
  12. public class NettyServerHandler extends ChannelInboundHandlerAdapter { 
  13.  
  14.     /** 
  15.      * 讀取客戶端發送的數據 
  16.      * 
  17.      * @param ctx 上下文對象, 含有通道channel,管道pipeline 
  18.      * @param msg 就是客戶端發送的數據 
  19.      * @throws Exception 
  20.      */ 
  21.     @Override 
  22.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  23.         System.out.println("服務器讀取線程 " + Thread.currentThread().getName()); 
  24.         //Channel channel = ctx.channel(); 
  25.         //ChannelPipeline pipeline = ctx.pipeline(); //本質是一個雙向鏈接, 出站入站 
  26.         //將 msg 轉成一個 ByteBuf,類似NIO 的 ByteBuffer 
  27.         ByteBuf buf = (ByteBuf) msg; 
  28.         System.out.println("客戶端發送消息是:" + buf.toString(CharsetUtil.UTF_8)); 
  29.     } 
  30.  
  31.     /** 
  32.      * 數據讀取完畢處理方法 
  33.      * 
  34.      * @param ctx 
  35.      * @throws Exception 
  36.      */ 
  37.     @Override 
  38.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
  39.         ByteBuf buf = Unpooled.copiedBuffer("HelloClient".getBytes(CharsetUtil.UTF_8)); 
  40.         ctx.writeAndFlush(buf); 
  41.     } 
  42.  
  43.     /** 
  44.      * 處理異常, 一般是需要關閉通道 
  45.      * 
  46.      * @param ctx 
  47.      * @param cause 
  48.      * @throws Exception 
  49.      */ 
  50.     @Override 
  51.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
  52.         ctx.close(); 
  53.     } 

這個類繼承了ChannelInboundHandlerAdapter的幾個方法:

  • channelRead方法:當客戶端與服務端連通好之后,客戶端發數據時,服務端會主動調用這個方法。
  • channelReadComplete方法:數據處理完畢的方法,ctx.writeAndFlush()就可以往客戶端寫回數據了。

客戶端代碼

  1. package com.niuh.netty.base; 
  2.  
  3. import io.netty.bootstrap.Bootstrap; 
  4. import io.netty.channel.ChannelFuture; 
  5. import io.netty.channel.ChannelInitializer; 
  6. import io.netty.channel.EventLoopGroup; 
  7. import io.netty.channel.nio.NioEventLoopGroup; 
  8. import io.netty.channel.socket.SocketChannel; 
  9. import io.netty.channel.socket.nio.NioSocketChannel; 
  10.  
  11. public class NettyClient { 
  12.     public static void main(String[] args) throws Exception { 
  13.         //客戶端需要一個事件循環組 
  14.         EventLoopGroup group = new NioEventLoopGroup(); 
  15.         try { 
  16.             //創建客戶端啟動對象 
  17.             //注意客戶端使用的不是ServerBootstrap而是Bootstrap 
  18.             Bootstrap bootstrap = new Bootstrap(); 
  19.             //設置相關參數 
  20.             bootstrap.group(group) //設置線程組 
  21.                     .channel(NioSocketChannel.class) // 使用NioSocketChannel作為客戶端的通道實現 
  22.                     .handler(new ChannelInitializer<SocketChannel>() { 
  23.                         @Override 
  24.                         protected void initChannel(SocketChannel ch) throws Exception { 
  25.                             //加入處理器 
  26.                             ch.pipeline().addLast(new NettyClientHandler()); 
  27.                         } 
  28.                     }); 
  29.  
  30.             System.out.println("netty client start。。"); 
  31.             //啟動客戶端去連接服務器端 
  32.             ChannelFuture cf = bootstrap.connect("127.0.0.1", 9000).sync(); 
  33.             //對通道關閉進行監聽 
  34.             cf.channel().closeFuture().sync(); 
  35.         } finally { 
  36.             group.shutdownGracefully(); 
  37.         } 
  38.     } 

客戶端自定義回調函數:

  1. package com.niuh.netty.base; 
  2.  
  3. import io.netty.buffer.ByteBuf; 
  4. import io.netty.buffer.Unpooled; 
  5. import io.netty.channel.ChannelHandlerContext; 
  6. import io.netty.channel.ChannelInboundHandlerAdapter; 
  7. import io.netty.util.CharsetUtil; 
  8.  
  9. public class NettyClientHandler extends ChannelInboundHandlerAdapter { 
  10.  
  11.     /** 
  12.      * 當客戶端連接服務器完成就會觸發該方法 
  13.      * 
  14.      * @param ctx 
  15.      * @throws Exception 
  16.      */ 
  17.     @Override 
  18.     public void channelActive(ChannelHandlerContext ctx) throws Exception { 
  19.         ByteBuf buf = Unpooled.copiedBuffer("HelloServer".getBytes(CharsetUtil.UTF_8)); 
  20.         ctx.writeAndFlush(buf); 
  21.     } 
  22.  
  23.     //當通道有讀取事件時會觸發,即服務端發送數據給客戶端 
  24.     @Override 
  25.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  26.         ByteBuf buf = (ByteBuf) msg; 
  27.         System.out.println("收到服務端的消息:" + buf.toString(CharsetUtil.UTF_8)); 
  28.         System.out.println("服務端的地址: " + ctx.channel().remoteAddress()); 
  29.     } 
  30.  
  31.     @Override 
  32.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
  33.         cause.printStackTrace(); 
  34.         ctx.close(); 
  35.     } 

與NettyServerHandler類似,其中的channelActive()方法是當客戶端與服務器連接完成時候就會執行的方法。

看完代碼,我們發現Netty架的目標就是讓你的業務邏輯從網絡基礎應用編碼中分離出來,讓你可以專 注業務的開發,而不需寫一大堆類似NIO的網絡處理操作。

ByteBuf 理解
從結構上來說,ByteBuf 由一串字節數組構成。數組中每個字節用來存放信息。

ByteBuf 提供了兩個索引,一個用于讀取數據,一個用于寫入數據。這兩個索引通過在字節數組中移動,來定位需要讀或者寫信息的位置。

  • 當從 ByteBuf 讀取時,它的 readerIndex(讀索引)將會根據讀取的字節數遞增。
  • 同樣,當寫 ByteBuf 時,它的 writerIndex 也會根據寫入的字節數進行遞增。

ByteBuf.png

需要注意的是極限的情況是 readerIndex 剛好讀到了 writerIndex 寫入的地方。如果 readerIndex 超過了 writerIndex 的時候,Netty 會拋出 IndexOutOf-BoundsException 異常。

示例代碼

  1. package com.niuh.netty.base; 
  2.  
  3. import io.netty.buffer.ByteBuf; 
  4. import io.netty.buffer.Unpooled; 
  5. import io.netty.util.CharsetUtil; 
  6.  
  7. public class NettyByteBuf { 
  8.     public static void main(String[] args) { 
  9.         // 創建byteBuf對象,該對象內部包含一個字節數組byte[10] 
  10.         // 通過readerindex和writerIndex和capacity,將buffer分成三個區域 
  11.         // 已經讀取的區域:[0,readerindex) 
  12.         // 可讀取的區域:[readerindex,writerIndex) 
  13.         // 可寫的區域: [writerIndex,capacity) 
  14.         ByteBuf byteBuf = Unpooled.buffer(10); 
  15.         System.out.println("byteBuf=" + byteBuf); 
  16.  
  17.         for (int i = 0; i < 8; i++) { 
  18.             byteBuf.writeByte(i); 
  19.         } 
  20.         System.out.println("byteBuf=" + byteBuf); 
  21.  
  22.         for (int i = 0; i < 5; i++) { 
  23.             System.out.println(byteBuf.getByte(i)); 
  24.         } 
  25.         System.out.println("byteBuf=" + byteBuf); 
  26.  
  27.         for (int i = 0; i < 5; i++) { 
  28.             System.out.println(byteBuf.readByte()); 
  29.         } 
  30.         System.out.println("byteBuf=" + byteBuf); 
  31.  
  32.  
  33.         //用Unpooled工具類創建ByteBuf 
  34.         ByteBuf byteBuf2 = Unpooled.copiedBuffer("hello,zhangsan!", CharsetUtil.UTF_8); 
  35.         //使用相關的方法 
  36.         if (byteBuf2.hasArray()) { 
  37.             byte[] content = byteBuf2.array(); 
  38.             //將 content 轉成字符串 
  39.             System.out.println(new String(content, CharsetUtil.UTF_8)); 
  40.             System.out.println("byteBuf=" + byteBuf2); 
  41.  
  42.             System.out.println(byteBuf2.readerIndex()); // 0 
  43.             System.out.println(byteBuf2.writerIndex()); // 12 
  44.             System.out.println(byteBuf2.capacity()); // 36 
  45.  
  46.             System.out.println(byteBuf2.getByte(0)); // 獲取數組0這個位置的字符h的ascii碼,h=104 
  47.  
  48.             int len = byteBuf2.readableBytes(); //可讀的字節數  12 
  49.             System.out.println("len=" + len); 
  50.  
  51.             //使用for取出各個字節 
  52.             for (int i = 0; i < len; i++) { 
  53.                 System.out.println((char) byteBuf2.getByte(i)); 
  54.             } 
  55.  
  56.             //范圍讀取 
  57.             System.out.println(byteBuf2.getCharSequence(0, 6, CharsetUtil.UTF_8)); 
  58.             System.out.println(byteBuf2.getCharSequence(6, 6, CharsetUtil.UTF_8)); 
  59.         } 
  60.     } 

PS:以上代碼提交在 Github :https://github.com/Niuh-Study/niuh-netty.git

責任編輯:姜華 來源: 今日頭條
相關推薦

2020-07-02 09:15:59

Netty內存RPC

2022-04-12 08:00:17

socket 編程網絡編程網絡 IO 模型

2021-07-16 11:35:20

Java線程池代碼

2022-04-11 10:56:43

線程安全

2024-09-04 16:19:06

語言模型統計語言模型

2025-04-21 04:00:00

2022-09-29 15:39:10

服務器NettyReactor

2017-12-05 17:44:31

機器學習CNN卷積層

2025-05-06 01:14:00

系統編程響應式

2024-01-03 13:39:00

JS,Javascrip算法

2023-10-18 10:55:55

HashMap

2025-04-11 05:55:00

2025-01-13 16:00:00

服務網關分布式系統架構

2021-10-11 11:58:41

Channel原理recvq

2023-05-29 08:12:38

2021-10-09 19:05:06

channelGo原理

2025-03-17 00:21:00

2024-05-31 08:10:58

Netty線程模型多路復用模型

2021-06-16 14:18:37

NettyReactor線程模型

2021-12-29 17:29:07

KubernetesEvents集群
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人精品鲁一区一区二区 | 亚洲第一区久久 | 中文字幕在线三区 | 免费a大片 | 性国产丰满麻豆videosex | 亚洲成人精品 | 亚洲一区在线免费观看 | 日韩欧美高清 | 久久久久久国模大尺度人体 | 亚洲国产精品久久久久秋霞不卡 | 亚洲精品乱码久久久久久蜜桃91 | 日韩av一区二区在线观看 | 婷婷91| 一级毛片免费看 | 日韩成人精品在线 | 欧美一区免费在线观看 | 国产欧美精品一区二区色综合 | 久久久久亚洲 | 精品国产欧美日韩不卡在线观看 | 欧美精品一区二区三区蜜桃视频 | 精品国产久 | 久久国产一区二区三区 | 久草网免费| 国产激情在线看 | 精品久久一区 | 在线不卡视频 | 欧美精品在线播放 | 一区二区三区中文字幕 | 亚洲自拍偷拍免费视频 | 羞羞的视频网站 | 人人九九精 | 给我免费的视频在线观看 | 久久久久久www | 阿v视频在线观看 | 日本三级全黄三级a | 国产在线一| 日韩精品久久 | 91久久精品一区二区二区 | 国产一区二区三区四区hd | 午夜影院| 久久久久久免费免费 |