1. 程式人生 > >netty(十七)原始碼分析之客戶端建立

netty(十七)原始碼分析之客戶端建立

相對於服務端,Netty客戶端的建立更加複雜,除了要考慮執行緒模型、非同步連線、客戶端連線超時等因素外,還需要對連線過程中的各種異常進行考慮。

下面我們直接分析客戶端連線操作:

首先要建立和初始化NioSocketChannel,程式碼如下:
    private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

從NioEventLoopGroup總獲取NioEventLoop,然後使用其作為引數建立NioSocketChannel,程式碼如下:
    Channel createChannel() {
        EventLoop eventLoop = group().next();
        return channelFactory().newChannel(eventLoop);
    }

初始化Channel之後,將其註冊到Selector上,程式碼如下:
        ChannelPromise regFuture = channel.newPromise();
        channel.unsafe().register(regFuture);

鏈路建立成功之後,發起非同步TCP連線,程式碼如下:
    private static void doConnect0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    if (localAddress == null) {
                        channel.connect(remoteAddress, promise);
                    } else {
                        channel.connect(remoteAddress, localAddress, promise);
                    }

由上述程式碼可以看出,從doConnnect0操作開始,連線操作切換到了Netty的NIO執行緒NioEventLoop中進行,此時客戶端返回,連線操作非同步執行。 doConnect0最終呼叫Headhandler的connect方法,程式碼如下:
        public void connect(
                ChannelHandlerContext ctx,
                SocketAddress remoteAddress, SocketAddress localAddress,
                ChannelPromise promise) throws Exception {
            unsafe.connect(remoteAddress, localAddress, promise);
        }

AbstractNioUnsafe的connect操作如下。
                if (doConnect(remoteAddress, localAddress)) {
                    fulfillConnectPromise(promise, wasActive);
                } else {

需要注意的是,SocketChannel執行connect()操作後有以下三種結果。 (1)連線成功,返回true; (2)暫時沒有連線上,服務端沒有返回ACK應答,連線結果不確定,返回false; (3)連線失敗,直接丟擲I/O異常。 如果是第二種結果,需要將NioSocketChannel中的selectionKey設定為OP_CONNECT,監聽連線結果。 非同步連線返回之後,需要判斷連線結果,如果連線成功,則觸發ChannelActive事件,程式碼如下(在fulfillConnectPromise方法中):
            if (!wasActive && isActive()) {
                pipeline().fireChannelActive();
            }

ChannelActive事件處理最終會將NioSocketChannel中的selectionKey設定為SelectionKey.OP_READ,用於監聽網路讀操作。


客戶端連線超時機制

對於SocketChannel介面,JDK並沒有提供連線超時機制,需要NIO框架或者使用者自己擴充套件實現。Netty利用定時器提供了客戶端連線超時控制功能,下面我們詳細看下該功能: 首先,使用者在建立Netty客戶端的時候,可以通過ChannelOption.CONNECT_TIMEOUT_MILLIS配置項設定連線超時時間,程式碼如下: b.group().opration(ChannelOption.CONNECT_TIMEOUT_MILLIS,3000); 發起連線的同時,啟動連線超時檢測定時器,程式碼如下(在AbstractNioChannel中):
                        connectTimeoutFuture = eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
                                ConnectTimeoutException cause =
                                        new ConnectTimeoutException("connection timed out: " + remoteAddress);
                                if (connectPromise != null && connectPromise.tryFailure(cause)) {
                                    close(voidPromise());
                                }
                            }
                        }, connectTimeoutMillis, TimeUnit.MILLISECONDS);

一旦超時定時器執行,說明客戶端連線超時,構造連線超時異常,將異常結果設定到connectPromise中,同時關閉客戶端連線,釋放控制代碼。 如果在連線超時前獲取到連線結果,則刪除連線超時定時器,防止其被觸發,程式碼如下:
                    promise.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if (future.isCancelled()) {
                                if (connectTimeoutFuture != null) {
                                    connectTimeoutFuture.cancel(false);
                                }

無論連線是否成功,只要獲取到連線結果,之後就刪除連線超時定時器。

相關推薦

netty原始碼分析客戶建立

相對於服務端,Netty客戶端的建立更加複雜,除了要考慮執行緒模型、非同步連線、客戶端連線超時等因素外,還需要對連線過程中的各種異常進行考慮。 下面我們直接分析客戶端連線操作: 首先要建立和初始化NioSocketChannel,程式碼如下: private Cha

一起學Nettynetty原始碼學習大話java NIO

沉澱了一個月安安心心地學習了家純大神的Jupiter(https://github.com/fengjiachun/Jupiter),感覺受益良多,感覺自己學習了這裡面的精華的50%,不是謙虛,而是無知,因為我不知道著裡面還有多少是我沒有理解的,也許我看懂了他的程式碼,但我

機器學習 kaggle競賽泰坦尼克號專案實戰-2

導航        想寫這篇部落格的由衷是做完幾個專案,有時對於圖的畫法和模型融合演算法原理理解還很膚淺,特此加深一下印象。 內容概覽 圖 pandas、matplotlib、seaborn 餅圖 直方圖

機器學習 關聯分析Apriori演算法

前言        目前隨著資料量迅速增多,從海量資料中尋找有價值的資訊帶來的成本也在不斷增加,傳統的搜尋資料方式已經不能滿足我們的需要,我們先來通過一個演算法看一下演算法時間複雜度快慢帶來的影響,通過計算耗時我們會有個感性

訊息中介軟體--RabbitMQ學習---高階特性死信佇列

死信佇列:DLX,Dead- Letter- Exchange 利用DLX,當訊息在一個佇列中變成死信( dead message)之後它能被重新 publish到另一個 Exchange,這個 Exchange就是DLX 死信佇列訊息變成死信有一下幾種情況

Spring 學習——Spring AOP返回通知、異常通知和環繞通知

返回通知 •無論連線點是正常返回還是丟擲異常, 後置通知都會執行. 如果只想在連線點返回的時候記錄日誌, 應使用返回通知代替後置通知.   在返回通知中訪問連線點的返回值 •在返回通知中, 只要將 returning 屬性新增到 @AfterReturning 註解中

Android 7.0 虛擬按鍵NavigationBar原始碼分析 View的建立流程

    最近有個需求是修改虛擬按鍵的單擊和長按效果。所以研究了下Android關於虛擬按鍵的實現流程。好記性不如爛筆頭,記錄如下。     首先,幾個重要的類: //實現 單個虛擬按鍵的 自定義ImageView     frameworks/base/packages/

netty原始碼分析ByteBuf

通過對netty的API的學習,可以更加遊刃有餘的使用netty的相關類庫 對原始碼的學習不僅能夠從原始碼層面掌握netty框架,方便日後的維護,拓展和定製而且可以起到觸類旁通的作用,拓展讀者的知識面,提升程式設計技能。當我們進行資料傳輸的時候,往往需要使用到緩衝區,常用的

Netty原始碼分析 ----- 心跳服務 IdleStateHandler 原始碼分析

什麼是心跳機制? 心跳說的是在客戶端和服務端在互相建立ESTABLISH狀態的時候,如何通過傳送一個最簡單的包來保持連線的存活,還有監控另一邊服務的可用性等。 心跳包的作用 保活Q:為什麼說心跳機制能保持連線的存活,它是叢集中或長連線中最為有效避免網路中斷的一個重要的保障措施?A:之所以說是&l

redis原始碼分析與思考——有序集合型別的命令實現(t_zset.c)

    有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時

python學習網站的編寫HTML,CSS,JS----------示例,構造一個網頁的框架,上部標題,登入,logo,左側選單,右側內容,原始碼

結果: 顏色為了明顯,所以較為難看,可以根據自己的需要進行更改 原始碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title

redis原始碼分析與思考——有序集合型別的命令實現(t_set.c)

    有序集合是集合的延伸,它儲存著集合元素的不可重複性,但不同的是,它是有序的,它利用每一個元素的分數來作為有序集合的排序依據,現在列出有序集合的命令: 有序集合命令 命令 對應操作 時間複

spring深入學習 IOC 分析各 scope 的 bean 建立

在 Spring 中存在著不同的 scope,預設是 singleton ,還有 prototype、request 等等其他的 scope,他們的初始化步驟是怎樣的呢?這個答案在這篇部落格中給出。 singleton Spring 的 scope 預設為 singleton,其初始化的程式

聊聊併發:concurrent包Condition原始碼分析

前言 在前幾篇文章中, 我們介紹了concurrent包中幾種鎖的實現機制,對其原始碼進行了分析,在介紹鎖的文章中,並沒有提及到Condition這個類,其實Condition的使用是與Lock繫結在一起的,本章,我們詳細瞭解一下Conditon的使用方式以及

Redis原始碼分析--- multi事務操作

        redis作為一非關係型資料庫,竟然同樣擁有與RDBMS的事務操作,不免讓我覺得比較驚訝。在redis就專門有檔案就是執行事務的相關操作的。也可以讓我們領略一下,在Redis的程式碼中是如何實現事務操作。首先亮出mulic.c下面的一些API。 /* ===

一起學Nettynetty原始碼學習netty server原始碼初讀

server端是使用了Reactor模式對nio進行了一些封裝,Reactor模式網上有很多資料,不贅述,瞭解了這個模式開始看原始碼 netty的版本是4.0.21.Final <dependency> <groupId>io.netty<

設計模式——迭代器模式ArrayList 集合應用原始碼分析

1 看一個具體的需求 編寫程式展示一個學校院系結構:需求是這樣,要在一個頁面中展示出學校的院系組成,一個學校有多個學院, 一個學院有多個系。如圖: 2 傳統的設計方案(類圖) 3 傳統的方式的問題分析 1) 將學院看做是學校的子類,系是學院的子類,這樣實

Linux系列教程——Linux權限管理文件系統系統屬性chattr權限和sudo命令

系統屬性 brush 選項 all 好的 幫助 博客 簡單 命令   上篇博客我們介紹了權限管理的ACL權限,通過設定 ACL 權限,我們為某個用戶指定某個文件的特定權限。這篇博客我們將介紹權限管理中用的比較多的兩個命令 chattr 和 sudo 。 1、設定文件系統

Hadoop學習MapReduce框架Partitoner分區

div get() 劃分 mapreduce ride 作用 程序 輸出 lin Partitioner分區類的作用是什麽? 在進行MapReduce計算時,有時候需要把最終的輸出數據分到不同的文件中,比如按照省份劃分的話,需要把同一省份的數據放到一個文件中;按照性別劃分

Hive學習Hive分析窗口函數(三) CUME_DIST和PERCENT_RANK

select rank com ble class mina src format () 這兩個序列分析函數不是很常用,這裏也練習一下。 數據準備 數據格式 cookie3.txt d1,user1,1000 d1,user2,2000 d1,user3,