您好,登錄后才能下訂單哦!
這篇文章主要介紹“netty的使用方法是什么”,在日常操作中,相信很多人在netty的使用方法是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”netty的使用方法是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Netty 是由 JBOSS 提供的一個異步的、 基于事件驅動的網絡編程框架。Netty 可以幫助你快速、 簡單的開發出一 個網絡應用, 相當于流程化及簡化了 NIO 的開發過程。知名的 Elasticsearch 、 Dubbo 框架內部都采用了 Netty。
NIO缺點
NIO 的類庫和 API 繁雜,使用麻煩。你需要熟練掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等.
可靠性不強,開發工作量和難度都非常大
NIO 的 Bug。例如 Epoll Bug,它會導致 Selector 空輪詢,最終導致 CPU 100%。
Netty優點
對各種傳輸協議提供統一的 API
高度可定制的線程模型——單線程、一個或多個線程池
更好的吞吐量,更低的等待延遲
更少的資源消耗
最小化不必要的內存拷貝
Reactor模式是基于事件驅動開發的,核心組成部分包括Reactor和線程池,其中Reactor負責監聽和分配事件,線程池負責處理事件。Netty線程模型就是Reactor模式的一個實現。
無論是 C++ 還是 Java 編寫的網絡框架,大多數都是基于 Reactor 模式進行設計和開發,Reactor 模式基于事件驅動,特別適合處理海量的 I/O 事件。
Reactor的數量和線程池的數量,又將Reactor分為三種模型:
單線程模型 (單Reactor單線程)
多線程模型 (單Reactor多線程)
主從多線程模型 (多Reactor多線程)。
Reactor 單線程模型,指的是所有的 IO 操作(接收請求、業務邏輯處理)都在同一個 NIO 線程上面完成。
NIO 線程的特點如下:
作為 NIO 服務端,接收客戶端的 TCP 連接
作為 NIO 客戶端,向服務端發起 TCP 連接;
讀取通信另一端的請求或者應答消息;
向通信另一端發送請求消息或者應答消息
Reactor內部通過selector 監控連接事件,收到事件后通過dispatch進行分發,如果是連接建立的事件,則由Acceptor處理,Acceptor通過accept接受連接,并創建一個Handler來處理連接后續的各種事件,如果是讀寫事件,直接調用連接對應的業務Handler來處理。
適用場景:
對于一些小容量應用場景,可以使用單線程模型。
但是對于高負載、大并發的應用場景卻不合適。 一個 NIO 線程同時處理成百上千的鏈路,性能上無法支撐,即便 NIO 線程的 CPU 負荷達到 100%,也無法滿足海量消息的編碼、解碼、讀取和發送。
Reactor多線程模型,仍然只有一個Nio線程用于監聽連接請求;而業務邏輯處理由一個線程池負責。
主線程中,Reactor對象通過selector監控連接事件,收到事件后通過dispatch進行分發,如果是連接建立事件,則由Acceptor處理,Acceptor通過accept接收連接,并創建一個Handler來處理后續事件,而Handler只負責響應事件,不進行業務操作,也就是只進行read讀取數據和write寫出數據,業務處理交給一個線程池進行處理。
線程池分配一個線程完成真正的業務處理,然后將響應結果交給主進程的Handler處理,Handler將結果發送給客戶端
適用場景:
在絕大多數場景下,Reactor 多線程模型都可以滿足性能需求;
但是,在極個別特殊場景中,一個 NIO 線程負責監聽和處理所有的客戶端連接可能會存在性能問題。例如并發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。在這類場景下,單獨一個線程可能會存在性能不足問題。
相對于多線程模型,服務端用于接收客戶端連接的不再是個 1 個單獨的 NIO 線程,而是一個獨立的 NIO 線程池。Acceptor 線程池僅僅只用于客戶端的登陸、握手和安全認證,一旦鏈路建立成功,就將鏈路注冊到后端 subReactor 線程池的 IO 線程上,由 IO 線程負責后續的 IO 操作。
存在多個Reactor,每個Reactor都有自己的selector選擇器,線程和dispatch
主線程中的mainReactor通過自己的selector監控連接建立事件,收到事件后通過Accpetor接收,將新的連接分配給某個子線程
子線程中的subReactor將mainReactor分配的連接加入連接隊列中通過自己的selector進行監聽,并創建一個Handler用于處理后續事件
Handler完成read->業務處理->send的完整業務流程
單線程模型就是只指定一個線程執行客戶端連接和讀寫操作,也就是在一個Reactor中完成,對應在Netty中的實現就是將NioEventLoopGroup線程數設置為1,核心代碼是:
NioEventLoopGroup group = new NioEventLoopGroup(1); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(group) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ServerHandlerInitializer());
工作流程如下:
多線程模型就是在一個單Reactor中進行客戶端連接處理,然后業務處理交給線程池,核心代碼如下:
NioEventLoopGroup eventGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(eventGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ServerHandlerInitializer());
工作流程如下:
主從多線程模型是有多個Reactor,也就是存在多個selector,所以我們定義一個bossGroup和一個workGroup,核心代碼如下:
NioEventLoopGroup bossGroup = new NioEventLoopGroup(); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ServerHandlerInitializer());
工作流程如下:
注意:其實在Netty中,bossGroup線程池最終還是只會隨機選擇一個線程用于處理客戶端連接,與此同時,NioServerSocetChannel綁定到bossGroup的線程中,NioSocketChannel綁定到workGroup的線程中
ChannelHandler 接口定義了許多事件處理的方法,可以通過重寫這些方法去實現具體的業務邏輯。經常需要自定義一個 Handler 類去繼承 ChannelInboundHandlerAdapter, 然后通過重寫相應方法實現業務邏輯
public void channelActive(ChannelHandlerContext ctx), 通道就緒事件 public void channelRead(ChannelHandlerContext ctx, Object msg), 通道讀取數據事件 public void channelReadComplete(ChannelHandlerContext ctx) , 數據讀取完畢事件 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause), 通道發生異常事件
ChannelPipeline 是一個 Handler 的集合, 它負責處理和攔截 inbound 或者 outbound 的事 件和操作, 相當于 一個貫穿 Netty 的鏈。
ChannelPipeline addFirst(ChannelHandler... handlers), 把一個業務處理類(handler) 添加到鏈中的第一個位置 ChannelPipeline addLast(ChannelHandler... handlers), 把一個業務處理類(handler) 添加到鏈中的最后一個位置
ChannelHandlerContext是事件處理器上下文對象 ,Pipeline鏈中的實際處理節點 。每個處理節點ChannelHandlerContext中包含一個具體的事件處理器ChannelHandler,同時ChannelHandlerContext中也綁定了對應的pipeline和Channel的信息
ChannelFuture close(), 關閉通道 ChannelOutboundInvoker flush(), 刷新 ChannelFuture writeAndFlush(Object msg) , 將數據寫到ChannelPipeline中,當前ChannelHandler的下一個ChannelHandler 開始處理(出站)
ChannelFuture用來表示 Channel 中異步 I/O 操作的結果, 在 Netty 中所有的 I/O 操作都是異步的, I/O 的調用會直接返回, 調用者并不能立刻獲得結果, 但是可以通過 ChannelFuture 來獲取 I/O 操作的處理狀態。
Channel channel(), 返回當前正在進行 IO 操作的通道 ChannelFuture sync(), 等待異步操作執行完畢
EventLoopGroup 是一組 EventLoop 的抽象, Netty 為了更好的利用多核 CPU 資源, 一般會有多個 EventLoop 同時工作, 每個EventLoop 維護著一個Selector 實例。
EventLoopGroup 提供 next 接口, 可以從組里面按照一定規則獲取其中一個 EventLoop 來處理任務。
在 Netty 服務器端編程中,我們一般都需要提供兩個EventLoopGroup, 例如: BossEventLoopGroup和WorkerEventLoopGroup。
ServerBootstrap 是 Netty 中的服務器端啟動助手,通過它可以完成服務器端的各種配置; Bootstrap 是 Netty 中 的客戶端啟動助手, 通過它可以完成客戶端的各種配置。
ServerBootstrap:
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup),該方法用于服務器端, 用來設置兩個 EventLoop public B channel(Class<? extends C> channelClass), 該方法用來設置一個服務器端的通道實現 public <T> B option(ChannelOption<T> option, T value), 用來給 ServerChannel 添加配置 public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value), 用來給接收到的通道添加配置 public ServerBootstrap childHandler(ChannelHandler childHandler), 該方法用來設置業務處理類(自定義的 handler) public ChannelFuture bind(int inetPort) , 該方法用于服務器端, 用來設置占用的端口號
Bootstrap:
public ChannelFuture connect(String inetHost, int inetPort) 該方法用于客戶端, 用來連接服務器端 public B group(EventLoopGroup group) , 該方法用于客戶端, 用來設置一個 EventLoop
服務端:
public static void main(String[] args) throws InterruptedException { // 1. 創建二個線程池對象 /** * bossGroup 負責接受用戶連接 */ NioEventLoopGroup bossGroup = new NioEventLoopGroup(); /** * workGroup 負責用戶的io讀寫操作 */ NioEventLoopGroup workGroup = new NioEventLoopGroup(); /** * 2.創建啟動引導類 */ ServerBootstrap serverBootstrap = new ServerBootstrap(); // 3.設置啟動引導類 // 設置組,第一個bossGroup負責連接, workerGroup負責連接之后的io處理 serverBootstrap.group(bossGroup,workGroup) // channel方法指定服務器監聽的通道類型 .channel(NioServerSocketChannel.class) //設置channel handler , 每一個客戶端連接后,給定一個監聽器進行處理 .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception { //獲取傳輸通道 ChannelPipeline pipeline = nioSocketChannel.pipeline(); //在通道上添加對通道的處理器 , 該處理器可能還是一個監聽器 pipeline.addFirst(new StringEncoder()); pipeline.addLast(new StringDecoder()); //監聽器隊列上添加我們自己的處理方式.. pipeline.addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception { System.out.println(msg); } }); } }); // 4.啟動引導類綁定端口 ChannelFuture future = serverBootstrap.bind(8888).sync(); // 5.關閉通道 Channel channel = future.channel(); channel.closeFuture().sync(); }
客戶端:
public static void main(String[] args) throws InterruptedException { // 1.創建連接池對象 NioEventLoopGroup group = new NioEventLoopGroup(); // 2. 創建客戶端的啟動引導類 Bootstrap bootstrap = new Bootstrap(); // 3. 配置啟動引導類 bootstrap.group(group) // 設置通道為nio .channel(NioSocketChannel.class) // 設置channel初始化監聽 .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) throws Exception { // 設置編碼 channel.pipeline().addLast(new StringEncoder()); } }); // 4.使用啟動引導類連接服務器,獲取一個channel Channel channel = bootstrap.connect("127.0.0.1", 8888).channel(); // 5.循環寫數據給服務器 while(true){ // 給服務器寫數據 channel.writeAndFlush("hello server.. this is clint say to you.."); Thread.sleep(2000); } }
使用Netty自定義實現Rpc
到此,關于“netty的使用方法是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。