原創申明:本文由公衆號【猿燈塔】原創,轉載請說明出處標註
今天是猿燈塔「365篇原創計劃」第九篇。面試
接下來的時間燈塔君持續更新Netty系列一共九篇promise
Netty 源碼解析(一): 開始微信
Netty 源碼解析(二): Netty 的 Channel異步
Netty 源碼解析(三): Netty的 Future 和 Promiseide
Netty 源碼解析(四): Netty 的 ChannelPipelineoop
Netty 源碼解析(五): Netty 的線程池分析this
Netty 源碼解析(六): Channel 的 register 操做spa
Netty 源碼解析(七): NioEventLoop 工做流程線程
Netty 源碼解析(八): 回到 Channel的 register 操做ssr
當前:Netty 源碼解析(九): connect過程和bind過程分析
今天呢!燈塔君跟你們講:
connect 過程和 bind過程分析
connect 過程和 bind 過程分析
上面咱們介紹的 register 操做很是關鍵,它創建起來了不少的東西,它是 Netty 中 NioSocketChannel 和 NioServerSocketChannel 開始工做的起點。這一節,咱們來講說 register 以後的 connect 操做和 bind 操做。這節很是簡單。
connect 過程分析
對於客戶端 NioSocketChannel 來講,前面 register 完成之後,就要開始 connect 了,這一步將鏈接到服務端。
`privateChannelFuturedoResolveAndConnect(finalSocketAddressremoteAddress,finalSocketAddresslocalAddress){
//這裏完成了register操做
finalChannelFutureregFuture=initAndRegister();
finalChannelchannel=regFuture.channel();
//這裏咱們不去糾結register操做是否isDone()
if(regFuture.isDone()){
if(!regFuture.isSuccess()){ returnregFuture;
}
//看這裏
returndoResolveAndConnect0(channel,remoteAddress,localAddress,channel.newPromise());
}else{ ....
}
} `
這裏你們本身一路點進去,我就不浪費篇幅了。最後,咱們會來到 AbstractChannel 的 connect 方法:
`@Override publicChannelFutureconnect(SocketAddressremoteAddress,ChannelPromisepromise){
returnpipeline.connect(remoteAddress,promise);
}`
咱們看到,connect 操做是交給 pipeline 來執行的。進入 pipeline 中,咱們會發現,connect 這種 Outbound 類型的操做,是從 pipeline 的 tail 開始的:
前面咱們介紹的 register 操做是 Inbound 的,是從 head 開始的
`@Override publicfinalChannelFutureconnect(SocketAddressremoteAddress,ChannelPromisepromise){
returntail.connect(remoteAddress,promise);
} `
接下來就是 pipeline 的操做了,從 tail 開始,執行 pipeline 上的 Outbound 類型的 handlers 的 connect(...) 方法,那麼真正的底層的 connect 的操做發生在哪裏呢?還記得咱們的 pipeline 的圖嗎?
刪除
從 tail 開始往前找 out 類型的 handlers,每通過一個 handler,都執行裏面的 connect() 方法,最後會到 head 中,由於 head 也是 Outbound 類型的,咱們須要的 connect 操做就在 head 中,它會負責調用 unsafe 中提供的 connect 方法:
`//HeadContext publicvoidconnect(
ChannelHandlerContextctx,
SocketAddressremoteAddress,SocketAddresslocalAddress,
ChannelPromisepromise)throwsException{
unsafe.connect(remoteAddress,localAddress,promise);
} `
接下來,咱們來看一看 connect 在 unsafe 類中所謂的底層操做:
`//AbstractNioChannel.AbstractNioUnsafe
@Override
publicfinalvoidconnect(
finalSocketAddressremoteAddress,finalSocketAddresslocalAddress,finalChannelPromisepromise){
......
booleanwasActive=isActive();
//你們本身點進去看doConnect方法 //這一步會作JDK底層的SocketChannelconnect,而後設置interestOps爲SelectionKey.OP_CONNECT
//返回值表明是否已經鏈接成功
if(doConnect(remoteAddress,localAddress)){
//處理鏈接成功的狀況
fulfillConnectPromise(promise,wasActive);
}else{
connectPromise=promise;
requestedRemoteAddress=remoteAddress;
//下面這塊代碼,在處理鏈接超時的狀況,代碼很簡單
//這裏用到了NioEventLoop的定時任務的功能,這個咱們以前一直都沒有介紹過,由於我以爲也不過重要
intconnectTimeoutMillis=config().getConnectTimeoutMillis();
if(connectTimeoutMillis>0){
connectTimeoutFuture=eventLoop().schedule(newRunnable(){
@Override
publicvoidrun(){
ChannelPromiseconnectPromise=AbstractNioChannel.this.connectPromise;
ConnectTimeoutExceptioncause=
newConnectTimeoutException("connectiontimedout:"+remoteAddress);
if(connectPromise!=null&&connectPromise.tryFailure(cause)){
close(voidPromise());
}
}
},connectTimeoutMillis,TimeUnit.MILLISECONDS);
}
promise.addListener(newChannelFutureListener(){
@Override publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(future.isCancelled()){
if(connectTimeoutFuture!=null){
connectTimeoutFuture.cancel(false);
}
connectPromise=null;
close(voidPromise()); } }
});
}
}catch(Throwablet){
promise.tryFailure(annotateConnectException(t,remoteAddress));
closeIfClosed();
}
}`
若是上面的 doConnect 方法返回 false,那麼後續是怎麼處理的呢?在上一節介紹的 register 操做中,channel 已經 register 到了 selector 上,只不過將 interestOps 設置爲了 0,也就是什麼都不監聽。而在上面的 doConnect 方法中,咱們看到它在調用底層的 connect 方法後,會設置 interestOps 爲 SelectionKey.OP_CONNECT。剩下的就是 NioEventLoop 的事情了,還記得 NioEventLoop 的 run() 方法嗎?也就是說這裏的 connect 成功之後,這個 TCP 鏈接就創建起來了,後續的操做會在 NioEventLoop.run() 方法中被 processSelectedKeys() 方法處理掉。
bind 過程分析
說完 connect 過程,咱們再來簡單看下 bind 過程:
`privateChannelFuturedoBind(finalSocketAddresslocalAddress){
//**前面說的initAndRegister**
finalChannelFutureregFuture=initAndRegister();
finalChannelchannel=regFuture.channel();
if(regFuture.cause()!=null){
returnregFuture;
}
if(regFuture.isDone()){
//register動做已經完成,那麼執行bind操做
ChannelPromisepromise=channel.newPromise();
doBind0(regFuture,channel,localAddress,promise);
returnpromise;
}else{
......
}
} `
而後一直往裏看,會看到,bind 操做也是要由 pipeline 來完成的: // AbstractChannel
`@Override publicChannelFuturebind(SocketAddresslocalAddress,ChannelPromisepromise){
returnpipeline.bind(localAddress,promise);
}
`
bind 操做和 connect 同樣,都是 Outbound 類型的,因此都是 tail 開始:
`@Override publicfinalChannelFuturebind(SocketAddresslocalAddress,ChannelPromisepromise){
returntail.bind(localAddress,promise);
}`
最後的 bind 操做又到了 head 中,由 head 來調用 unsafe 提供的 bind 方法:
`@Override publicvoidbind( ChannelHandlerContextctx,SocketAddresslocalAddress,ChannelPromisepromise)
throwsException{
unsafe.bind(localAddress,promise);
}`
感興趣的讀者本身去看一下 unsafe 中的 bind 方法,很是簡單,bind 操做也不是什麼異步方法,咱們就介紹到這裏了。本節很是簡單,就是想和你們介紹下 Netty 中各類操做的套路。
365天干貨不斷微信搜索「猿燈塔」第一時間閱讀,回覆【資料】【面試】【簡歷】有我準備的一線大廠面試資料和簡歷模板