Netty進階內部元件詳解
Netty進階
Bootstrap、ServerBootstrap
- Bootstrap意思是引導,一個Netty應用通常由一個Bootstrap開始,主要作用是配置整個Netty程式,串聯各個元件,Netty中ServerBootstrap是服務端的啟動引導類,Bootstrap是客戶端引導類
常用方法
方法 | 說明 |
---|---|
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) | 伺服器端:設定兩個EventLoopGroup |
public B channel(Class<? extends C> channelClass) | 伺服器端:設定通道的實現 |
public B option(ChannelOption option, T value) | 伺服器端:為Server Channel新增配置 |
public ServerBootstrap childOption(ChannelOption childOption, T value) | 伺服器端:給接收的通道新增配置 |
public ServerBootstrap childHandler(ChannelHandler childHandler) | 伺服器端:設定業務處理型別(自定義handler) |
public ChannelFuture bind(int inetPort) | 伺服器端:設定服務埠號 |
public B group(EventLoopGroup group) | 客戶端:設定一個EventLoopGroup |
public ChannelFuture connect(String inetHost, int inetPort) | 客戶端:連線服務端 |
NioEventLoopGroup
NioEventLoopGroupd 執行緒的個數預設是系統 執行緒數 * 2
電腦的執行緒數
EventLoopGroup不傳入引數初始化的執行緒數
原始碼
如果nThreads傳入是空預設會採用DEFAULT_EVENT_LOOP_THREADS
DEFAULT_EVENT_LOOP_THREADS屬性是靜態程式碼塊,NettyRuntime.availableProcessors()方法就可以獲取當前機器處理器的核數
指定NioEventLoop數量
Channel
- 在Netty網路通訊元件,能夠用於執行網路I/O操作
- 通過Channel可獲得當前網路連線的通道的狀態
- 通過Channel可以獲得網路連線的配置引數(例如接收緩衝區大小)
- Channel提供非同步I/O操作(如建立連線,讀寫,埠繫結),非同步呼叫意味著任何I/O操作都將會立刻返回,並且不保證呼叫結束時所請求的I/O操作已完成
- 呼叫立即返回一個ChannelFuture例項,通過註冊監聽器到ChannelFuture上,可以I/O操作成功,失敗或者取消時回撥通知呼叫方
- 不同協議,不同阻塞型別都有不同的Channel型別對應
常用Channel型別
Channel型別 | 協議 |
---|---|
NioSocketChannel | 非同步的客戶端TCP Socket連線 |
NioServerSocketChannel | 非同步的服務端TCP Socket連線 |
NioDatagramChannel | 非同步的UDP連線 |
NioSctpChannel | 非同步的客戶端Sctp連線 |
NioSctpServerChannel | 非同步的服務端Sctp連線 |
channel與pipeline關係
從服務端cxt中分別獲取channel和pipeline
檢視channel屬性
檢視pipeline屬性
結論:通過斷點我們發現channel與pipeline是相互對應的,channel可以獲取到pipeline,pipeline也可以獲取到channel
Pipeline、ChannelPipeline
- ChannelPipeline是一個Handler的集合,它負責處理和攔截inbound或者outbound的事件和操作,相當於一個貫穿Netty的鏈條(ChannelPipeline儲存ChannelHandler的List,用於處理或者攔截Channel的入站事件和出站事件操作)
- ChannelPipeline例項了一種高階形式的攔截過濾器模式,使使用者可以完全控制事件的處理方式,以及Channel中各個的ChannelHandler如何相互互動
Netty中每一個CHannel都有且僅有一個ChannelPipeline對應
ChannelPipeline其實是一個雙向列表,可以通過head中的next找到下一個需要執行的ChannelHandler,相反我我們可以通過tail的prev得到上一個執行過的ChannelHandler,ChannelHandler真實的型別是ChannelHandlerContext
入/出站說明
入站事件和出站事件在一個雙向連結串列中,入站事件會從連結串列的head往後傳遞到最後一個入站的handler,出站事件會從連結串列的tail往前傳遞到最前一個出站的handler,兩種型別的handler相互不干擾
打斷點檢視
常用方法
方法 | 說明 |
---|---|
ChannelPipeline addFirst(String name, ChannelHandler handler) | 把業務處理類(handler)新增到鏈中的第一個位置 |
ChannelPipeline addLast(String name, ChannelHandler handler) | 把業務處理類(handler)新增到鏈中的最後一個位置 |
ChannelHandler
- ChannelHandler是一個介面,處理I/O事件或攔截I/O操作,在ChannelPipeline(業務處理鏈)會存在很多個Handler,處理完成一個Handler後會交給下一個Handler處理
- ChannelHandler本身沒有提供很多方法,因為這個介面有許多的方法需要實現,便使用期間,可以繼承他的子類
ChannelHandler關鍵實現類
- ChannelInboundHandler用於處理入站I/O事件
- ChannelOutboundHandler用於處理出站I/O事件
介面卡
- ChannelInboundHandlerAdapter用於處理入站I/O事件
- ChannelOutboundHandlerAdapter用於處理出站I/O事件
常用方法
方法 | 說明 |
---|---|
public void channelRegistered(ChannelHandlerContext ctx) | 通道註冊觸發 |
public void channelUnregistered(ChannelHandlerContext ctx) | 通道登出觸發 |
public void channelActive(ChannelHandlerContext ctx) | 通道就緒觸發 |
public void channelInactive(ChannelHandlerContext ctx) | 通道非活動狀態觸發 |
public void channelRead(ChannelHandlerContext ctx, Object msg) | 通道有可讀事件觸發 |
public void channelReadComplete(ChannelHandlerContext ctx) | 通道讀取完畢後觸發 |
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) | 通道發生異常觸發 |
ChannelHandlerContext
- 儲存Channel相關的所有上下文資訊,相同關聯一個ChannelHandler物件
- 即ChannelHandlerContext中包含一個具體的事件處理器ChannelHandler,同時也包含了對應的pipeline和Channel的資訊,方便對ChannelHandler進行呼叫
常用方法
方法 | 說明 |
---|---|
ChannelFuture close() | 關閉通道 |
ChannelHandlerContext flush() | 重新整理 |
ChannelFuture writeAndFlush(Object msg) | 將資料寫到ChannelPipeline中當前ChannelHandler,下一個ChannelHandler開始處理 |
ChannelOption
- Netty在建立Channel例項後,一般需要設定ChannelOption引數
常用引數
引數 | 說明 |
---|---|
ChannelOption.SO_BACKLOG | 對應TCP/IP協議listen函式的backlog引數,用來初始化伺服器可連線佇列大小,服務端處理客戶端連線請求是順序處理的,所以同一時間只能處理一個客戶端連線,多個客戶端來的時候,客戶端不能處理客戶端連線請求放在佇列中等待處理,backlog引數指定佇列的大小 |
ChannelOption.SO_KEEPALIVE | 一直儲存連線活動狀態 |
EventLoopGroup、NioEventLoopGroup
- EventLoopGroup是一組EventLoop的抽象,Netty為了更好的利用多核CPU資源,一般會有多個EventLoop同時工作,每個EventLoop維護著一個Selector例項
- EventLoopGroup提供next介面,可以從組裡面按照一定規則獲取其中一個EventLoop來處理任務,在Netty伺服器程式設計中,我們一般需要提供兩個EventLoopGroup,一個用於處理Accept事件一個處理其它事件,如:BossEventLoopGroup和WorkerEventLoopGroup
Unpooled
Netty提供一個專門用來操作緩衝區(即Netty的資料日期)的工具類,ByteBuf比NIO的buyeBuffer使用起來更加方便,效能也比較高
使用例子1
使用例子2
TaskQueue
在前面的案例中一直沒有使用到TaskQueue,TaskQueue有什麼用呢?
TaskQueue是任務佇列,在Netty模型中事件處理都是由Handler處理,但如果我們在Handler中有一些業務邏輯複雜的事情要處理,最終還是會導致當前處理的執行緒發生阻塞,這時就需要使用到TaskQueue,進入Handler,直接將這些複雜的業務邏輯提交到TaskQueue佇列中,就會有對應的執行緒非同步處理,這樣子可以做到立刻對客戶端請求響應,無需等待業務執行完畢響應,等業務處理成功後再次回應客戶端
TaskQueue使用場景
- 使用者程式自定義的普通任務
- 使用者自定義定時任務
- 非當前Reactor執行緒呼叫Channel的各種用法
使用者程式自定義的普通任務
結論:我們可以看到伺服器端不阻塞了,提交請求後業務程式碼被提交到了,當前channel().eventLoop()的TaskQueue中後,就直接執行channelReadComplete返回了,然後等待10秒後服務端把業務邏輯處理完成後就會再次返回
如果不相信已經提交到TaskQueue,可以打一個斷點看一下,可以發現ctx.channel().eventLoop().execute執行了後,TaskQueue的Size是1
使用者自定義定時任務
結論:業務邏輯程式碼先會被提交到一個scheduleTaskQueue的佇列中,在等待5秒後才會被執行裡面的程式碼
如果不相信已經提交到scheduleTaskQueue,可以打一個斷點看一下,可以發現ctx.channel().eventLoop().schedule執行了後,scheduleTaskQueue的Size是1
非當前Reactor執行緒呼叫Channel的各種用法
在訊息推送的時候就需要使用到該方法,因為每一個使用者他們繫結的eventLoop不一定都一樣,這樣需要維護一個channel的引用,根據使用者標識獲取到對應的channel這樣子我們就可以取到對應的eventLoop然後使用Write方法就可以給相應的使用者進行一個訊息推送
服務端需要儲存對應使用者的channel
非同步模型
- 非同步和同步的概念是相對的,當一個非同步過程呼叫發出後,呼叫者不會立刻得到結果,等實際處理這個呼叫的元件完成後,通過狀態、通知、回撥等來通知呼叫者(JS中回撥函式)
- Netty中的I/O操作都是非同步的,包括Bind、Write、Connect等操作會簡單的返回一個ChannelFuture
- 呼叫者並不能立刻獲得結果,而是通過Future-Listener機制,使用者可以方便的主動獲取或者通過通知機制獲取IO操作結果
- Netty的非同步模型是建立在future和callback的上面的,callback就是回撥(前端Ajax就是典型例子通過回撥來獲取對應的值),終點來說future,核心思想:假如一個方法,計算過程非常耗時,等待方法返回顯然不合適,那麼可以這樣子,你呼叫該方法時我不管有沒有執行完成,我先給你返回一個Future,後續如果你想知道方法執行的怎麼樣了,可以通過Future去監聽該方法執行(即Future-Listener機制)
Future總結
- Future是非同步執行結果,可以通過它提供的方法檢測執行是否完成
- ChannelFuture是我們使用最多的,ChannelFuture是一個介面,我們可以給ChannelFuture新增監聽器,當監聽的事件被觸發時,就會通知監聽器
Future-Listener機制
- 當Future物件剛剛建立時,處於一個非完成狀態,呼叫者可以通過返回的ChannelFuture獲取操作執行的狀態,監聽函式來執行完成後的操作
常見方法
方法名 | 說明 |
---|---|
isDone | 獲取當前操作是否完成 |
isSuccess | 獲取已完成操作是否成功 |
getCause | 獲取已完成操作失敗的原因 |
isCancelled | 獲取已完成的當前操作是否被取消 |
addListener | 註冊監聽器後,才能呼叫上面的方法 |
使用案例
相關文章
- 一文詳解 Netty 元件Netty元件
- Java內部類詳解--匿名內部類Java
- Java基礎內部類4-內部類進階Java
- CSS進階內容—盒子和陰影詳解CSS
- CSS進階內容—浮動和定位詳解CSS
- Java 內部類詳解Java
- Java內部類詳解Java
- Java內部類詳解-- 成員內部類Java
- vue元件詳解(五)——元件高階用法Vue元件
- React HOC高階元件詳解React元件
- Java內部類詳解--區域性內部類Java
- Java 內部類使用詳解Java
- Java基礎-內部類詳解Java
- React 進階(四)事件詳解React事件
- 詳解前端進階指南教程前端
- Redis入門--進階詳解Redis
- netty系列之:NIO和netty詳解Netty
- Day26:內部類的詳解
- ElasticSearch 文件(document)內部機制詳解Elasticsearch
- netty系列之:netty中的Channel詳解Netty
- netty系列之:netty中的ByteBuf詳解Netty
- three.js內部拖拽例子全詳解JS
- Java中的靜態內部類詳解Java
- React 進階(三) 高階元件React元件
- Docker內部元件介紹Docker元件
- Kubernetes 內部元件工作原理元件
- netty 之 telnet HelloWorld 詳解Netty
- Netty中的ChannelHander詳解Netty
- Netty中的ByteBuf詳解Netty
- netty系列之:channelHandlerContext詳解NettyContext
- netty系列之:channelPipeline詳解Netty
- Dart語法詳解(三)——進階篇Dart
- Vue3內建元件Teleport用法詳解Vue元件
- Redis內部資料結構詳解(4)——ziplistRedis資料結構
- Android進階;Handler訊息機制詳解Android
- Vue 進階教程之:詳解 v-modelVue
- netty元件模型Netty元件模型
- 前端進階課程之跨域問題詳解前端跨域