溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Netty Client啟動(dòng)流程是怎樣的

發(fā)布時(shí)間:2022-01-06 15:31:58 來(lái)源:億速云 閱讀:219 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“Netty Client啟動(dòng)流程是怎樣的”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Netty Client啟動(dòng)流程是怎樣的”吧!

Client啟動(dòng)流程揭秘

1、探秘的入口:netty-client demo

這里用netty-exmaple中的EchoClient來(lái)作為例子:

public final class EchoClient {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new EchoClientHandler());
                 }
             });

            ChannelFuture f = b.connect(HOST, PORT).sync();

            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

代碼沒(méi)有什么獨(dú)特的地方,我們上一篇文章時(shí)也梳理過(guò)Netty網(wǎng)絡(luò)編程的一些套路,這里就不再贅述了。 (忘記的小朋友可以查看Netty系列文章中查找~)

上面的客戶端代碼雖然簡(jiǎn)單, 但是卻展示了Netty 客戶端初始化時(shí)所需的所有內(nèi)容:

  • EventLoopGroupNetty服務(wù)端或者客戶端,都必須指定EventLoopGroup,客戶端指定的是NioEventLoopGroup

  • Bootstrap: Netty客戶端啟動(dòng)類,負(fù)責(zé)客戶端的啟動(dòng)和初始化過(guò)程

  • channel()類型:指定Channel的類型,因?yàn)檫@里是客戶端,所以使用的是NioSocketChannel,服務(wù)端會(huì)使用NioServerSocketChannel

  • Handler:設(shè)置數(shù)據(jù)的處理器

  • bootstrap.connect(): 客戶端連接netty服務(wù)的方法

2、NioEventLoopGroup 流程解析

我們先從NioEventLoopGroup開(kāi)始,一行行代碼解析,先看看其類結(jié)構(gòu):

Netty Client啟動(dòng)流程是怎樣的

上面是大致的類結(jié)構(gòu),而 EventLoop 又繼承自EventLoopGroup,所以類的大致結(jié)構(gòu)我們可想而知。這里一些核心邏輯會(huì)在MultithreadEventExecutorGroup中,包含EventLoopGroup的創(chuàng)建和初始化操作等。

接著從NioEventLoopGroup構(gòu)造方法開(kāi)始看起,一步步往下跟(代碼都只展示重點(diǎn)的部分,省去很多暫時(shí)不需要關(guān)心的代碼,以下代碼都遵循這個(gè)原則):

EventLoopGroup group = new NioEventLoopGroup();

public NioEventLoopGroup() {
    this(0);
}

public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {
    this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

這里通過(guò)調(diào)用this()super()方法一路往下傳遞,期間會(huì)構(gòu)造一些默認(rèn)屬性,一直傳遞到MultithreadEventExecutorGroup類中,接著往西看。

2.1、MultithreadEventExecutorGroup

上面構(gòu)造函數(shù)有一個(gè)重要的參數(shù)傳遞:DEFAULT_EVENT_LOOP_THREADS,這個(gè)值默認(rèn)是CPU核數(shù) * 2

為什么要傳遞這個(gè)參數(shù)呢?我們之前說(shuō)過(guò)EventLoopGroup可以理解成一個(gè)線程池,MultithreadEventExecutorGroup有一個(gè)線程數(shù)組EventExecutor[] children屬性,而傳遞過(guò)來(lái)的DEFAULT_EVENT_LOOP_THREADS就是數(shù)組的長(zhǎng)度。

先看下MultithreadEventExecutorGroup中的構(gòu)造方法:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
    
    children = new EventExecutor[nThreads];
    
    for (int i = 0; i < nThreads; i ++) {
        children[i] = newChild(executor, args);
    }
    
    // ... 省略
}

這段代碼執(zhí)行邏輯可以理解為:

  • 通過(guò)ThreadPerTaskExecutor構(gòu)造一個(gè)Executor執(zhí)行器,后面會(huì)細(xì)說(shuō),里面包含了線程執(zhí)行的execute()方法

  • 接著創(chuàng)建一個(gè)EventExecutor數(shù)組對(duì)象,大小為傳遞進(jìn)來(lái)的threads數(shù)量,這個(gè)所謂的EventExecutor可以理解為我們的EventLoop,在這個(gè)demo中就是NioEventLoop對(duì)象

  • 最后調(diào)用 newChild 方法逐個(gè)初始化EventLoopGroup中的EventLoop對(duì)象

上面只是大概說(shuō)了下MultithreadEventExecutorGroup中的構(gòu)造方法做的事情,后面還會(huì)一個(gè)個(gè)詳細(xì)展開(kāi),先不用著急,我們先有個(gè)整體的認(rèn)知就好。

再回到MultithreadEventExecutorGroup中的構(gòu)造方法入?yún)⒅校袀€(gè)EventExecutorChooserFactory對(duì)象,這里面是有個(gè)很亮眼的細(xì)節(jié)設(shè)計(jì),通過(guò)它我們來(lái)洞悉Netty的良苦用心。

2.1、亮點(diǎn)設(shè)計(jì):DefaultEventExecutorChooserFactory

Netty Client啟動(dòng)流程是怎樣的

EventExecutorChooserFactory這個(gè)類的作用是用來(lái)選擇EventLoop執(zhí)行器的,我們知道EventLoopGroup是一個(gè)包含了CPU * 2個(gè)數(shù)量的EventLoop數(shù)組對(duì)象,那每次選擇EventLoop來(lái)執(zhí)行任務(wù)是選擇數(shù)組中的哪一個(gè)呢?

我們看一下這個(gè)類的具體實(shí)現(xiàn),紅框中都是需要重點(diǎn)查看的地方:

Netty Client啟動(dòng)流程是怎樣的

DefaultEventExecutorChooserFactory是一個(gè)選擇器工廠類,調(diào)用里面的next()方法達(dá)到一個(gè)輪詢選擇的目的。

數(shù)組的長(zhǎng)度是length,執(zhí)行第n次,取數(shù)組中的哪個(gè)元素就是對(duì)length取余

Netty Client啟動(dòng)流程是怎樣的

繼續(xù)回到代碼的實(shí)現(xiàn),這里的優(yōu)化就是在于先通過(guò)isPowerOfTwo()方法判斷數(shù)組的長(zhǎng)度是否為2的n次冪,判斷的方式很巧妙,使用val & -val == val,這里我不做過(guò)多的解釋,網(wǎng)上還有很多判斷2的n次冪的優(yōu)秀解法,我就不班門弄斧了。(可參考:https://leetcode-cn.com/problems/power-of-two/solution/2de-mi-by-leetcode/)

當(dāng)然我認(rèn)為這里還有更容易理解的一個(gè)算法:x & (x - 1) == 0 大家可以看下面的圖就懂了,這里就不延展了:

Netty Client啟動(dòng)流程是怎樣的

BUT!!! 這里為什么要去煞費(fèi)苦心的判斷數(shù)組的長(zhǎng)度是2的n次冪?

不知道小伙伴們是否還記得大明湖畔HashMap?一般我們要求HashMap數(shù)組的長(zhǎng)度需要是2的n次冪,因?yàn)樵?code>key值尋找數(shù)組位置的方法:(n - 1) & hash n是數(shù)組長(zhǎng)度,這里如果數(shù)組長(zhǎng)度是2的n次冪就可以通過(guò)位運(yùn)算來(lái)提升性能,當(dāng)length為2的n次冪時(shí)下面公式是等價(jià)的:

n & (length - 1) <=> n % length

還記得上面說(shuō)過(guò),數(shù)組的長(zhǎng)度默認(rèn)都是CPU * 2,而一般服務(wù)器CPU核心數(shù)都是2、4、8、16等等,所以這一個(gè)小優(yōu)化就很實(shí)用了,再仔細(xì)想想,原來(lái)數(shù)組長(zhǎng)度的初始化也是很講究的。

這里位運(yùn)算的好處就是效率遠(yuǎn)遠(yuǎn)高于與運(yùn)算,Netty針對(duì)于這個(gè)小細(xì)節(jié)都做了優(yōu)化,真是太棒了。

2.3、線程執(zhí)行器:ThreadPerTaskExecutor

接著看下ThreadPerTaskExecutor線程執(zhí)行器,每次執(zhí)行任務(wù)都會(huì)通過(guò)它來(lái)創(chuàng)建一個(gè)線程實(shí)體。

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }
        this.threadFactory = threadFactory;
    }

    @Override
    public void execute(Runnable command) {
        threadFactory.newThread(command).start();
    }
}

傳遞進(jìn)來(lái)的threadFactoryDefaultThreadFactory,這里面會(huì)構(gòu)造NioEventLoop線程命名規(guī)則為nioEventLoop-1-xxx,我們就不細(xì)看這個(gè)了。當(dāng)線程執(zhí)行的時(shí)候會(huì)調(diào)用execute()方法,這里會(huì)創(chuàng)建一個(gè)FastThreadLocalThread線程,具體看代碼:

public class DefaultThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
        return t;
    }

    protected Thread newThread(Runnable r, String name) {
        return new FastThreadLocalThread(threadGroup, r, name);
    }
}

這里通過(guò)newThread()來(lái)創(chuàng)建一個(gè)線程,然后初始化線程對(duì)象數(shù)據(jù),最終會(huì)調(diào)用到Thread.init()中。

2.4、EventLoop初始化

接著繼續(xù)看MultithreadEventExecutorGroup構(gòu)造方法:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    children = new EventExecutor[nThreads];
    for (int i = 0; i < nThreads; i ++) {
        children[i] = newChild(executor, args);
        // .... 省略部分代碼
    }
}

上面代碼的最后一部分是 newChild 方法, 這個(gè)是一個(gè)抽象方法, 它的任務(wù)是實(shí)例化 EventLoop 對(duì)象. 我們跟蹤一下它的代碼, 可以發(fā)現(xiàn), 這個(gè)方法在 NioEventLoopGroup 類中實(shí)現(xiàn)了, 其內(nèi)容很簡(jiǎn)單:

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    if (selectorProvider == null) {
        throw new NullPointerException("selectorProvider");
    }
    if (strategy == null) {
        throw new NullPointerException("selectStrategy");
    }
    provider = selectorProvider;
    final SelectorTuple selectorTuple = openSelector();
    selector = selectorTuple.selector;
    unwrappedSelector = selectorTuple.unwrappedSelector;
    selectStrategy = strategy;
}

其實(shí)就是實(shí)例化一個(gè) NioEventLoop 對(duì)象, 然后返回。NioEventLoop構(gòu)造函數(shù)中會(huì)保存provider和事件輪詢器selector,在其父類中還會(huì)創(chuàng)建一個(gè)MpscQueue隊(duì)列,然后保存線程執(zhí)行器executor。

再回過(guò)頭來(lái)想一想,MultithreadEventExecutorGroup 內(nèi)部維護(hù)了一個(gè) EventExecutor[] children數(shù)組, NettyEventLoopGroup 的實(shí)現(xiàn)機(jī)制其實(shí)就建立在 MultithreadEventExecutorGroup 之上。

每當(dāng) Netty 需要一個(gè) EventLoop 時(shí), 會(huì)調(diào)用 next() 方法從EventLoopGroup數(shù)組中獲取一個(gè)可用的 EventLoop對(duì)象。其中next方法的實(shí)現(xiàn)是通過(guò)NioEventLoopGroup.next()來(lái)完成的,就是用的上面有過(guò)講解的通過(guò)輪詢算法來(lái)計(jì)算得出的。

最后總結(jié)一下整個(gè) EventLoopGroup 的初始化過(guò)程:

Netty Client啟動(dòng)流程是怎樣的

  • EventLoopGroup(其實(shí)是MultithreadEventExecutorGroup) 內(nèi)部維護(hù)一個(gè)類型為 EventExecutor children 數(shù)組,數(shù)組長(zhǎng)度是nThreads

  • 如果我們?cè)趯?shí)例化 NioEventLoopGroup 時(shí), 如果指定線程池大小, 則 nThreads 就是指定的值, 反之是處理器核心數(shù) * 2

  • MultithreadEventExecutorGroup 中會(huì)調(diào)用 newChild 抽象方法來(lái)初始化 children 數(shù)組

  • 抽象方法 newChild 是在 NioEventLoopGroup 中實(shí)現(xiàn)的, 它返回一個(gè) NioEventLoop 實(shí)例.

  • NioEventLoop 屬性:

    • SelectorProvider provider 屬性: NioEventLoopGroup 構(gòu)造器中通過(guò) SelectorProvider.provider() 獲取一個(gè) SelectorProvider

    • Selector selector 屬性: NioEventLoop 構(gòu)造器中通過(guò)調(diào)用通過(guò) selector = provider.openSelector() 獲取一個(gè) selector 對(duì)象.

2.5、NioSocketChannel

Netty中,Channel是對(duì)Socket的抽象,每當(dāng)Netty建立一個(gè)連接后,都會(huì)有一個(gè)與其對(duì)應(yīng)的Channel實(shí)例。

我們?cè)陂_(kāi)頭的Demo中,設(shè)置了channel(NioSocketChannel.class),NioSocketChannel的類結(jié)構(gòu)如下:

Netty Client啟動(dòng)流程是怎樣的

接著分析代碼,當(dāng)我們調(diào)用b.channel()時(shí)實(shí)際上會(huì)進(jìn)入AbstractBootstrap.channel()邏輯,接著看AbstractBootstrap中代碼:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

public ReflectiveChannelFactory(Class<? extends T> clazz) {
    if (clazz == null) {
        throw new NullPointerException("clazz");
    }
    this.clazz = clazz;
}

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    if (channelFactory == null) {
        throw new NullPointerException("channelFactory");
    }
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }

    this.channelFactory = channelFactory;
    return self();
}

可以看到,這里ReflectiveChannelFactory其實(shí)就是返回我們指定的channelClass:NioSocketChannel, 然后指定AbstractBootstrap中的channelFactory = new ReflectiveChannelFactory()

2.6、Channel初始化流程

到了這一步,我們已經(jīng)知道NioEventLoopGroupchannel()的流程,接著來(lái)看看Channel的 初始化流程,這也是Netty客戶端啟動(dòng)的的核心流程之一:

ChannelFuture f = b.connect(HOST, PORT).sync();

接著就開(kāi)始從b.connect()為入口一步步往后跟,先看下NioSocketChannel構(gòu)造的整體流程:

Netty Client啟動(dòng)流程是怎樣的

connet往后梳理下整體流程:

Bootstrap.connect -> Bootstrap.doResolveAndConnect -> AbstractBootstrap.initAndRegister

final ChannelFuture initAndRegister() {
    Channel channel = channelFactory.newChannel();
    init(channel);
    
    ChannelFuture regFuture = config().group().register(channel);
    return regFuture;
}

為了更易讀,這里代碼都做了簡(jiǎn)化,只保留了一些重要的代碼。

緊接著我們看看channelFactory.newChannel()做了什么,這里channelFactoryReflectiveChannelFactory,我們?cè)谏厦娴恼鹿?jié)分析過(guò):

@Override
public T newChannel() {
    try {
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}

這里的clazzNioSocketChannel,同樣是在上面章節(jié)講到過(guò),這里是調(diào)用NioSocketChannel的構(gòu)造函數(shù)然后初始化一個(gè)Channel實(shí)例。

public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
    public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }

    public NioSocketChannel(SelectorProvider provider) {
        this(newSocket(provider));
    }

    private static SocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openSocketChannel();
        } catch (IOException e) {
            throw new ChannelException("Failed to open a socket.", e);
        }
    }
}

這里其實(shí)也很簡(jiǎn)單,就是創(chuàng)建一個(gè)Java NIO SocketChannel而已,接著看看NioSocketChannel的父類還做了哪些事情,這里梳理下類的關(guān)系:

NioSocketChannel -> extends AbstractNioByteChannel -> exntends AbstractNioChannel

public abstract class AbstractNioChannel extends AbstractChannel {
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
        super(parent, ch, SelectionKey.OP_READ);
    }

    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        ch.configureBlocking(false);
    }
}

這里會(huì)調(diào)用父類的構(gòu)造參數(shù),并且傳遞readInterestOp = SelectionKey.OP_READ:,這里還有一個(gè)很重要的點(diǎn),配置 Java NIO SocketChannel 為非阻塞的,我們之前在NIO章節(jié)的時(shí)候講解過(guò),這里也不再贅述。

接著繼續(xù)看AbstractChannel的構(gòu)造函數(shù):

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }
}

這里創(chuàng)建一個(gè)ChannelId,創(chuàng)建一個(gè)Unsafe對(duì)象,這里的Unsafe并不是Java中的Unsafe,后面也會(huì)講到。然后創(chuàng)建一個(gè)ChannelPipeline,后面也會(huì)講到,到了這里,一個(gè)完整的NioSocketChannel 就初始化完成了,我們?cè)賮?lái)總結(jié)一下:

  • NettySocketChannel 會(huì)與 Java 原生的 SocketChannel 綁定在一起;

  • 會(huì)注冊(cè) Read 事件;

  • 會(huì)為每一個(gè) Channel 分配一個(gè) channelId;

  • 會(huì)為每一個(gè) Channel 創(chuàng)建一個(gè)Unsafe對(duì)象;

  • 會(huì)為每一個(gè) Channel 分配一個(gè) ChannelPipeline;

2.7、Channel 注冊(cè)流程

還是回到最上面initAndRegister方法,我們上面都是在分析里面newChannel的操作,這個(gè)方法是NioSocketChannel創(chuàng)建的一個(gè)流程,接著我們?cè)诶^續(xù)跟init()register()的過(guò)程:

 public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    final ChannelFuture initAndRegister() {
        Channel channel = channelFactory.newChannel();
        init(channel);
        ChannelFuture regFuture = config().group().register(channel);
    }
}

init()就是將一些參數(shù)optionsattrs設(shè)置到channel中,我們重點(diǎn)需要看的是register方法,其調(diào)用鏈為:

AbstractBootstrap.initAndRegister -> MultithreadEventLoopGroup.register -> SingleThreadEventLoop.register -> AbstractUnsafe.register

這里最后到了unsaferegister()方法,最終調(diào)用到AbstractNioChannel.doRegister():

@Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
        return;
    }
}

javaChannel()就是Java NIO中的SocketChannel,這里是將SocketChannel注冊(cè)到與eventLoop相關(guān)聯(lián)的selector上。

Netty Client啟動(dòng)流程是怎樣的

最后我們整理一下服務(wù)啟動(dòng)的整體流程:

  1. initAndRegister()初始化并注冊(cè)什么呢?

  • channelFactory.newChannel()

  • 通過(guò)反射創(chuàng)建一個(gè) NioSocketChannel

  • Java 原生 Channel 綁定到 NettyChannel

  • 注冊(cè) Read 事件

  • Channel 分配 id

  • Channel 創(chuàng)建 unsafe對(duì)象

  • Channel 創(chuàng)建 ChannelPipeline(默認(rèn)是 head<=>tail 的雙向鏈表)

  1. `init(channel)``

  • Bootstrap 中的配置設(shè)置到 Channel

  1. register(channel)

  • Channel 綁定到一個(gè) EventLoop

  • Java 原生 Channel、NettyChannel、Selector 綁定到 SelectionKey

  • 觸發(fā) Register 相關(guān)的事件

2.8 unsafe初始化

上面有提到過(guò)在初始化Channel的過(guò)程中會(huì)創(chuàng)建一個(gè)Unsafe的對(duì)象,然后綁定到Channel上:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

newUnsafe直接調(diào)用到了NioSocketChannel中的方法:

@Override
protected AbstractNioUnsafe newUnsafe() {
    return new NioSocketChannelUnsafe();
}

NioSocketChannelUnsafeNioSocketChannel中的一個(gè)內(nèi)部類,然后向上還有幾個(gè)父類繼承,這里主要是對(duì)應(yīng)到相關(guān)Java底層的Socket操作。

2.9 pipeline初始化

我們還是回到pipeline初始化的過(guò)程,來(lái)看一下newChannelPipeline()的具體實(shí)現(xiàn):

protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

protected DefaultChannelPipeline(Channel channel) {
    this.channel = ObjectUtil.checkNotNull(channel, "channel");
    succeededFuture = new SucceededChannelFuture(channel, null);
    voidPromise =  new VoidChannelPromise(channel, true);

    tail = new TailContext(this);
    head = new HeadContext(this);

    head.next = tail;
    tail.prev = head;
}

我們調(diào)用 DefaultChannelPipeline 的構(gòu)造器, 傳入了一個(gè) channel, 而這個(gè) channel 其實(shí)就是我們實(shí)例化的 NioSocketChannel。

DefaultChannelPipeline 會(huì)將這個(gè) NioSocketChannel 對(duì)象保存在channel 字段中. DefaultChannelPipeline 中, 還有兩個(gè)特殊的字段, 即 headtail, 而這兩個(gè)字段是一個(gè)雙向鏈表的頭和尾. 其實(shí)在 DefaultChannelPipeline 中, 維護(hù)了一個(gè)以 AbstractChannelHandlerContext 為節(jié)點(diǎn)的雙向鏈表, 這個(gè)鏈表是 Netty 實(shí)現(xiàn) Pipeline 機(jī)制的關(guān)鍵.

關(guān)于 DefaultChannelPipeline 中的雙向鏈表以及它所起的作用, 我們會(huì)在后續(xù)章節(jié)詳細(xì)講解。這里只是對(duì)pipeline做個(gè)初步的認(rèn)識(shí)。

HeadContext 的繼承層次結(jié)構(gòu)如下所示:

Netty Client啟動(dòng)流程是怎樣的

TailContext 的繼承層次結(jié)構(gòu)如下所示:

Netty Client啟動(dòng)流程是怎樣的

我們可以看到, 鏈表中 head 是一個(gè) ChannelOutboundHandler, 而 tail 則是一個(gè) ChannelInboundHandler.

3.0、客戶端connect過(guò)程

客戶端連接的入口方法還是在Bootstrap.connect()中,上面也分析過(guò)一部分內(nèi)容,請(qǐng)求的具體流程是:

Bootstrap.connect() -> AbstractChannel.coonnect() -> NioSocketChannel.doConnect()

public static boolean connect(final SocketChannel socketChannel, final SocketAddress remoteAddress)
            throws IOException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
            @Override
            public Boolean run() throws IOException {
                return socketChannel.connect(remoteAddress);
            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}

看到這里,還是用Java NIO SocketChannel發(fā)送的connect請(qǐng)求進(jìn)行客戶端連接請(qǐng)求。

到此,相信大家對(duì)“Netty Client啟動(dòng)流程是怎樣的”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI