成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Netty 源碼分析之 一 揭開 Bootstrap 神秘的紅蓋頭 (服務(wù)器端)

張金寶 / 830人閱讀

摘要:目錄源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端源碼分析之一揭開神秘的紅蓋頭服務(wù)器

目錄

Netty 源碼分析之 番外篇 Java NIO 的前生今世

Java NIO 的前生今世 之一 簡(jiǎn)介

Java NIO 的前生今世 之二 NIO Channel 小結(jié)

Java NIO 的前生今世 之三 NIO Buffer 詳解

Java NIO 的前生今世 之四 NIO Selector 詳解

Netty 源碼分析之 零 磨刀不誤砍柴工 源碼分析環(huán)境搭建

Netty 源碼分析之 一 揭開 Bootstrap 神秘的紅蓋頭

Netty 源碼分析之 一 揭開 Bootstrap 神秘的紅蓋頭 (客戶端)

Netty 源碼分析之 一 揭開 Bootstrap 神秘的紅蓋頭 (服務(wù)器端)

服務(wù)器端

在分析客戶端的代碼時(shí), 我們已經(jīng)對(duì) Bootstrap 啟動(dòng) Netty 有了一個(gè)大致的認(rèn)識(shí), 那么接下來分析服務(wù)器端時(shí), 就會(huì)相對(duì)簡(jiǎn)單一些了.
首先還是來看一下服務(wù)器端的啟動(dòng)代碼:

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(new EchoServerHandler());
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

和客戶端的代碼相比, 沒有很大的差別, 基本上也是進(jìn)行了如下幾個(gè)部分的初始化:

EventLoopGroup: 不論是服務(wù)器端還是客戶端, 都必須指定 EventLoopGroup. 在這個(gè)例子中, 指定了 NioEventLoopGroup, 表示一個(gè) NIO 的EventLoopGroup, 不過服務(wù)器端需要指定兩個(gè) EventLoopGroup, 一個(gè)是 bossGroup, 用于處理客戶端的連接請(qǐng)求; 另一個(gè)是 workerGroup, 用于處理與各個(gè)客戶端連接的 IO 操作.

ChannelType: 指定 Channel 的類型. 因?yàn)槭欠?wù)器端, 因此使用了 NioServerSocketChannel.

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

Channel 的初始化過程

我們?cè)诜治隹蛻舳说?Channel 初始化過程時(shí), 已經(jīng)提到, Channel 是對(duì) Java 底層 Socket 連接的抽象, 并且知道了客戶端的 Channel 的具體類型是 NioSocketChannel, 那么自然的, 服務(wù)器端的 Channel 類型就是 NioServerSocketChannel 了.
那么接下來我們按照分析客戶端的流程對(duì)服務(wù)器端的代碼也同樣地分析一遍, 這樣也方便我們對(duì)比一下服務(wù)器端和客戶端有哪些不一樣的地方.

Channel 類型的確定

同樣的分析套路, 我們已經(jīng)知道了, 在客戶端中, Channel 的類型其實(shí)是在初始化時(shí), 通過 Bootstrap.channel() 方法設(shè)置的, 服務(wù)器端自然也不例外.
在服務(wù)器端, 我們調(diào)用了 ServerBootstarap.channel(NioServerSocketChannel.class), 傳遞了一個(gè) NioServerSocketChannel Class 對(duì)象. 這樣的話, 按照和分析客戶端代碼一樣的流程, 我們就可以確定, NioServerSocketChannel 的實(shí)例化是通過 BootstrapChannelFactory 工廠類來完成的, 而 BootstrapChannelFactory 中的 clazz 字段被設(shè)置為了 NioServerSocketChannel.class, 因此當(dāng)調(diào)用 BootstrapChannelFactory.newChannel() 時(shí):

@Override
public T newChannel() {
    // 刪除 try 塊
    return clazz.newInstance();
}

就獲取到了一個(gè) NioServerSocketChannel 的實(shí)例.

最后我們也來總結(jié)一下:

ServerBootstrap 中的 ChannelFactory 的實(shí)現(xiàn)是 BootstrapChannelFactory

生成的 Channel 的具體類型是 NioServerSocketChannel.
Channel 的實(shí)例化過程, 其實(shí)就是調(diào)用的 ChannelFactory.newChannel 方法, 而實(shí)例化的 Channel 的具體的類型又是和在初始化 ServerBootstrap 時(shí)傳入的 channel() 方法的參數(shù)相關(guān). 因此對(duì)于我們這個(gè)例子中的服務(wù)器端的 ServerBootstrap 而言, 生成的的 Channel 實(shí)例就是 NioServerSocketChannel.

NioServerSocketChannel 的實(shí)例化過程

首先還是來看一下 NioServerSocketChannel 的實(shí)例化過程.
下面是 NioServerSocketChannel 的類層次結(jié)構(gòu)圖:

首先, 我們來看一下它的默認(rèn)的構(gòu)造器. 和 NioSocketChannel 類似, 構(gòu)造器都是調(diào)用了 newSocket 來打開一個(gè) Java 的 NIO Socket, 不過需要注意的是, 客戶端的 newSocket 調(diào)用的是 openSocketChannel, 而服務(wù)器端的 newSocket 調(diào)用的是 openServerSocketChannel. 顧名思義, 一個(gè)是客戶端的 Java SocketChannel, 一個(gè)是服務(wù)器端的 Java ServerSocketChannel.

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    return provider.openServerSocketChannel();
}

public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

接下來會(huì)調(diào)用重載的構(gòu)造器:

public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

這個(gè)構(gòu)造其中, 調(diào)用父類構(gòu)造器時(shí), 傳入的參數(shù)是 SelectionKey.OP_ACCEPT. 作為對(duì)比, 我們回想一下, 在客戶端的 Channel 初始化時(shí), 傳入的參數(shù)是 SelectionKey.OP_READ. 有 Java NIO Socket 開發(fā)經(jīng)驗(yàn)的朋友就知道了, Java NIO 是一種 Reactor 模式, 我們通過 selector 來實(shí)現(xiàn) I/O 的多路復(fù)用復(fù)用. 在一開始時(shí), 服務(wù)器端需要監(jiān)聽客戶端的連接請(qǐng)求, 因此在這里我們?cè)O(shè)置了 SelectionKey.OP_ACCEPT, 即通知 selector 我們對(duì)客戶端的連接請(qǐng)求感興趣.

接著和客戶端的分析一下, 會(huì)逐級(jí)地調(diào)用父類的構(gòu)造器 NioServerSocketChannel <- AbstractNioMessageChannel <- AbstractNioChannel <- AbstractChannel.
同樣的, 在 AbstractChannel 中會(huì)實(shí)例化一個(gè) unsafe 和 pipeline:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    unsafe = newUnsafe();
    pipeline = new DefaultChannelPipeline(this);
}

不過, 這里有一點(diǎn)需要注意的是, 客戶端的 unsafe 是一個(gè) AbstractNioByteChannel#NioByteUnsafe 的實(shí)例, 而在服務(wù)器端時(shí), 因?yàn)?AbstractNioMessageChannel 重寫了newUnsafe 方法:

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

因此在服務(wù)器端, unsafe 字段其實(shí)是一個(gè) AbstractNioMessageChannel#AbstractNioUnsafe 的實(shí)例.
我們來總結(jié)一下, 在 NioServerSocketChannsl 實(shí)例化過程中, 所需要做的工作:

調(diào)用 NioServerSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER) 打開一個(gè)新的 Java NIO ServerSocketChannel

AbstractChannel(Channel parent) 中初始化 AbstractChannel 的屬性:

parent 屬性置為 null

unsafe 通過newUnsafe() 實(shí)例化一個(gè) unsafe 對(duì)象, 它的類型是 AbstractNioMessageChannel#AbstractNioUnsafe 內(nèi)部類

pipeline 是 new DefaultChannelPipeline(this) 新創(chuàng)建的實(shí)例.

AbstractNioChannel 中的屬性:

SelectableChannel ch 被設(shè)置為 Java ServerSocketChannel, 即 NioServerSocketChannel#newSocket 返回的 Java NIO ServerSocketChannel.

readInterestOp 被設(shè)置為 SelectionKey.OP_ACCEPT

SelectableChannel ch 被配置為非阻塞的 ch.configureBlocking(false)

NioServerSocketChannel 中的屬性:

ServerSocketChannelConfig config = new NioServerSocketChannelConfig(this, javaChannel().socket())

ChannelPipeline 初始化

服務(wù)器端和客戶端的 ChannelPipeline 的初始化一致, 因此就不再多帶帶分析了.

Channel 的注冊(cè)

服務(wù)器端和客戶端的 Channel 的注冊(cè)過程一致, 因此就不再多帶帶分析了.

關(guān)于 bossGroup 與 workerGroup

在客戶端的時(shí)候, 我們只提供了一個(gè) EventLoopGroup 對(duì)象, 而在服務(wù)器端的初始化時(shí), 我們?cè)O(shè)置了兩個(gè) EventLoopGroup, 一個(gè)是 bossGroup, 另一個(gè)是 workerGroup. 那么這兩個(gè) EventLoopGroup 都是干什么用的呢? 其實(shí)呢, bossGroup 是用于服務(wù)端 的 accept 的, 即用于處理客戶端的連接請(qǐng)求. 我們可以把 Netty 比作一個(gè)飯店, bossGroup 就像一個(gè)像一個(gè)前臺(tái)接待, 當(dāng)客戶來到飯店吃時(shí), 接待員就會(huì)引導(dǎo)顧客就坐, 為顧客端茶送水等. 而 workerGroup, 其實(shí)就是實(shí)際上干活的啦, 它們負(fù)責(zé)客戶端連接通道的 IO 操作: 當(dāng)接待員 招待好顧客后, 就可以稍做休息, 而此時(shí)后廚里的廚師們(workerGroup)就開始忙碌地準(zhǔn)備飯菜了.
關(guān)于 bossGroup 與 workerGroup 的關(guān)系, 我們可以用如下圖來展示:

首先, 服務(wù)器端 bossGroup 不斷地監(jiān)聽是否有客戶端的連接, 當(dāng)發(fā)現(xiàn)有一個(gè)新的客戶端連接到來時(shí), bossGroup 就會(huì)為此連接初始化各項(xiàng)資源, 然后從 workerGroup 中選出一個(gè) EventLoop 綁定到此客戶端連接中. 那么接下來的服務(wù)器與客戶端的交互過程就全部在此分配的 EventLoop 中了.

口說無憑, 我們還是以源碼說話吧.
首先在ServerBootstrap 初始化時(shí), 調(diào)用了 b.group(bossGroup, workerGroup) 設(shè)置了兩個(gè) EventLoopGroup, 我們跟蹤進(jìn)去看一下:

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    super.group(parentGroup);
    ...
    this.childGroup = childGroup;
    return this;
}

顯然, 這個(gè)方法初始化了兩個(gè)字段, 一個(gè)是 group = parentGroup, 它是在 super.group(parentGroup) 中初始化的, 另一個(gè)是 childGroup = childGroup. 接著我們啟動(dòng)程序調(diào)用了 b.bind 方法來監(jiān)聽一個(gè)本地端口. bind 方法會(huì)觸發(fā)如下的調(diào)用鏈:

AbstractBootstrap.bind -> AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister

AbstractBootstrap.initAndRegister 是我們的老朋友了, 我們?cè)诜治隹蛻舳顺绦驎r(shí), 和它打過很多交到了, 我們?cè)賮砘仡櫼幌逻@個(gè)方法吧:

final ChannelFuture initAndRegister() {
    final Channel channel = channelFactory().newChannel();
    ... 省略異常判斷
    init(channel);
    ChannelFuture regFuture = group().register(channel);
    return regFuture;
}

這里 group() 方法返回的是上面我們提到的 bossGroup, 而這里的 channel 我們也已經(jīng)分析過了, 它是一個(gè)是一個(gè) NioServerSocketChannsl 實(shí)例, 因此我們可以知道, group().register(channel) 將 bossGroup 和 NioServerSocketChannsl 關(guān)聯(lián)起來了.
那么 workerGroup 是在哪里與 NioSocketChannel 關(guān)聯(lián)的呢?
我們繼續(xù)看 init(channel) 方法:

@Override
void init(Channel channel) throws Exception {
    ...
    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry, Object>[] currentChildOptions;
    final Entry, Object>[] currentChildAttrs;

    p.addLast(new ChannelInitializer() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }
            pipeline.addLast(new ServerBootstrapAcceptor(
                    currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
        }
    });
}

init 方法在 ServerBootstrap 中重寫了, 從上面的代碼片段中我們看到, 它為 pipeline 中添加了一個(gè) ChannelInitializer, 而這個(gè) ChannelInitializer 中添加了一個(gè)關(guān)鍵的 ServerBootstrapAcceptor handler. 關(guān)于 handler 的添加與初始化的過程, 我們留待下一小節(jié)中分析, 我們現(xiàn)在關(guān)注一下 ServerBootstrapAcceptor 類.
ServerBootstrapAcceptor 中重寫了 channelRead 方法, 其主要代碼如下:

@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;
    child.pipeline().addLast(childHandler);
    ...
    childGroup.register(child).addListener(...);
}

ServerBootstrapAcceptor 中的 childGroup 是構(gòu)造此對(duì)象是傳入的 currentChildGroup, 即我們的 workerGroup, 而 Channel 是一個(gè) NioSocketChannel 的實(shí)例, 因此這里的 childGroup.register 就是將 workerGroup 中的摸個(gè) EventLoop 和 NioSocketChannel 關(guān)聯(lián)了. 既然這樣, 那么現(xiàn)在的問題是, ServerBootstrapAcceptor.channelRead 方法是怎么被調(diào)用的呢? 其實(shí)當(dāng)一個(gè) client 連接到 server 時(shí), Java 底層的 NIO ServerSocketChannel 會(huì)有一個(gè) SelectionKey.OP_ACCEPT 就緒事件, 接著就會(huì)調(diào)用到 NioServerSocketChannel.doReadMessages:

@Override
protected int doReadMessages(List buf) throws Exception {
    SocketChannel ch = javaChannel().accept();
    ... 省略異常處理
    buf.add(new NioSocketChannel(this, ch));
    return 1;
}

在 doReadMessages 中, 通過 javaChannel().accept() 獲取到客戶端新連接的 SocketChannel, 接著就實(shí)例化一個(gè) NioSocketChannel, 并且傳入 NioServerSocketChannel 對(duì)象(即 this), 由此可知, 我們創(chuàng)建的這個(gè) NioSocketChannel 的父 Channel 就是 NioServerSocketChannel 實(shí)例 .
接下來就經(jīng)由 Netty 的 ChannelPipeline 機(jī)制, 將讀取事件逐級(jí)發(fā)送到各個(gè) handler 中, 于是就會(huì)觸發(fā)前面我們提到的 ServerBootstrapAcceptor.channelRead 方法啦.

handler 的添加過程

服務(wù)器端的 handler 的添加過程和客戶端的有點(diǎn)區(qū)別, 和 EventLoopGroup 一樣, 服務(wù)器端的 handler 也有兩個(gè), 一個(gè)是通過 handler() 方法設(shè)置 handler 字段, 另一個(gè)是通過 childHandler() 設(shè)置 childHandler 字段. 通過前面的 bossGroup 和 workerGroup 的分析, 其實(shí)我們?cè)谶@里可以大膽地猜測(cè): handler 字段與 accept 過程有關(guān), 即這個(gè) handler 負(fù)責(zé)處理客戶端的連接請(qǐng)求; 而 childHandler 就是負(fù)責(zé)和客戶端的連接的 IO 交互.
那么實(shí)際上是不是這樣的呢? 來, 我們繼續(xù)通過代碼證明.

關(guān)于 bossGroup 與 workerGroup 小節(jié)中, 我們提到, ServerBootstrap 重寫了 init 方法, 在這個(gè)方法中添加了 handler:

@Override
void init(Channel channel) throws Exception {
    ...
    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry, Object>[] currentChildOptions;
    final Entry, Object>[] currentChildAttrs;

    p.addLast(new ChannelInitializer() {
        @Override
        public void initChannel(Channel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = handler();
            if (handler != null) {
                pipeline.addLast(handler);
            }
            pipeline.addLast(new ServerBootstrapAcceptor(
                    currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
        }
    });
}

上面代碼的 initChannel 方法中, 首先通過 handler() 方法獲取一個(gè) handler, 如果獲取的 handler 不為空,則添加到 pipeline 中. 然后接著, 添加了一個(gè) ServerBootstrapAcceptor 實(shí)例. 那么這里 handler() 方法返回的是哪個(gè)對(duì)象呢? 其實(shí)它返回的是 handler 字段, 而這個(gè)字段就是我們?cè)诜?wù)器端的啟動(dòng)代碼中設(shè)置的:

b.group(bossGroup, workerGroup)
 ...
 .handler(new LoggingHandler(LogLevel.INFO))

那么這個(gè)時(shí)候, pipeline 中的 handler 情況如下:

根據(jù)我們?cè)瓉矸治隹蛻舳说慕?jīng)驗(yàn), 我們指定, 當(dāng) channel 綁定到 eventLoop 后(在這里是 NioServerSocketChannel 綁定到 bossGroup)中時(shí), 會(huì)在 pipeline 中發(fā)出 fireChannelRegistered 事件, 接著就會(huì)觸發(fā) ChannelInitializer.initChannel 方法的調(diào)用.
因此在綁定完成后, 此時(shí)的 pipeline 的內(nèi)如如下:

前面我們?cè)诜治?bossGroup 和 workerGroup 時(shí), 已經(jīng)知道了在 ServerBootstrapAcceptor.channelRead 中會(huì)為新建的 Channel 設(shè)置 handler 并注冊(cè)到一個(gè) eventLoop 中, 即:

@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;
    child.pipeline().addLast(childHandler);
    ...
    childGroup.register(child).addListener(...);
}

而這里的 childHandler 就是我們?cè)诜?wù)器端啟動(dòng)代碼中設(shè)置的 handler:

b.group(bossGroup, workerGroup)
 ...
 .childHandler(new ChannelInitializer() {
     @Override
     public void initChannel(SocketChannel ch) throws Exception {
         ChannelPipeline p = ch.pipeline();
         if (sslCtx != null) {
             p.addLast(sslCtx.newHandler(ch.alloc()));
         }
         //p.addLast(new LoggingHandler(LogLevel.INFO));
         p.addLast(new EchoServerHandler());
     }
 });

后續(xù)的步驟就沒有什么好說的了, 當(dāng)這個(gè)客戶端連接 Channel 注冊(cè)后, 就會(huì)觸發(fā) ChannelInitializer.initChannel 方法的調(diào)用, 此后的客戶端連接的 ChannelPipeline 狀態(tài)如下:

最后我們來總結(jié)一下服務(wù)器端的 handler 與 childHandler 的區(qū)別與聯(lián)系:

在服務(wù)器 NioServerSocketChannel 的 pipeline 中添加的是 handler 與 ServerBootstrapAcceptor.

當(dāng)有新的客戶端連接請(qǐng)求時(shí), ServerBootstrapAcceptor.channelRead 中負(fù)責(zé)新建此連接的 NioSocketChannel 并添加 childHandler 到 NioSocketChannel 對(duì)應(yīng)的 pipeline 中, 并將此 channel 綁定到 workerGroup 中的某個(gè) eventLoop 中.

handler 是在 accept 階段起作用, 它處理客戶端的連接請(qǐng)求.

childHandler 是在客戶端連接建立以后起作用, 它負(fù)責(zé)客戶端連接的 IO 交互.

下面我們用一幅圖來總結(jié)一下服務(wù)器端的 handler 添加流程:

后記

這是 Netty 源碼分析 系列教程的第一篇, 按我的計(jì)劃, 這一篇文章是一個(gè)簡(jiǎn)述性質(zhì)的, 即這里會(huì)涉及到 Netty 各個(gè)功能模塊, 但是我只是簡(jiǎn)單地提了一下, 而沒有深入地探索它們內(nèi)部的實(shí)現(xiàn)機(jī)理. 之所以這樣做, 第一, 是因?yàn)槿绻簧蟻砭蛷募?xì)節(jié)分析, 那么未免會(huì)陷入各種瑣碎的細(xì)節(jié)中難以自拔; 第二, 我想給讀者展示一個(gè)一個(gè)完整的 Netty 的運(yùn)行流程, 讓讀者從一個(gè)整體上對(duì) Netty 有一個(gè)感性的認(rèn)識(shí).
此篇文章涉及的模塊比較多, 面比較廣, 因此寫起來難免有一點(diǎn)跳躍, 并且我感覺寫著寫著見見有點(diǎn)不知所云, 邏輯混亂了, 汗. 唉, 還是感覺自己功力不夠, hold 不住.
接下來的幾篇文章, 我會(huì)根據(jù) Netty 的各個(gè)模塊深入分析一下, 希望以后的文章能夠組織的調(diào)理更加清晰一些.

本文由 yongshun 發(fā)表于個(gè)人博客, 采用 署名-相同方式共享 3.0 中國(guó)大陸許可協(xié)議.
Email: [email protected]
本文標(biāo)題為: Netty 源碼分析之 一 揭開 Bootstrap 神秘的紅蓋頭 (服務(wù)器端)
本文鏈接為: https://segmentfault.com/a/1190000007283053

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66228.html

相關(guān)文章

  • Netty 源碼分析 揭開 Bootstrap 神秘的紅蓋頭 (客戶)

    摘要:目錄源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端源碼分析之一揭開神秘的紅蓋頭服務(wù)器 目錄 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 簡(jiǎn)介 Java NIO 的前生今世 ...

    zhaot 評(píng)論0 收藏0
  • 源碼下無秘密 ── 做最好的 Netty 源碼分析教程

    摘要:背景在工作中雖然我經(jīng)常使用到庫但是很多時(shí)候?qū)Φ囊恍└拍钸€是處于知其然不知其所以然的狀態(tài)因此就萌生了學(xué)習(xí)源碼的想法剛開始看源碼的時(shí)候自然是比較痛苦的主要原因有兩個(gè)第一網(wǎng)上沒有找到讓我滿意的詳盡的源碼分析的教程第二我也是第一次系統(tǒng)地學(xué)習(xí)這么大代 背景 在工作中, 雖然我經(jīng)常使用到 Netty 庫, 但是很多時(shí)候?qū)?Netty 的一些概念還是處于知其然, 不知其所以然的狀態(tài), 因此就萌生了學(xué)...

    shenhualong 評(píng)論0 收藏0
  • Netty 源碼分析 三 我就是大名鼎鼎的 EventLoop()

    摘要:目錄源碼之下無秘密做最好的源碼分析教程源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端 目錄 源碼之下無秘密 ── 做最好的 Netty 源碼分析教程 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NI...

    livem 評(píng)論0 收藏0
  • Netty 源碼分析 二 貫穿Netty 的大動(dòng)脈 ── ChannelPipeline ()

    摘要:目錄源碼之下無秘密做最好的源碼分析教程源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端 目錄 源碼之下無秘密 ── 做最好的 Netty 源碼分析教程 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NI...

    tunny 評(píng)論0 收藏0
  • Netty 源碼分析 二 貫穿Netty 的大動(dòng)脈 ── ChannelPipeline (二)

    摘要:目錄源碼之下無秘密做最好的源碼分析教程源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端 目錄 源碼之下無秘密 ── 做最好的 Netty 源碼分析教程 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NI...

    kamushin233 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

張金寶

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<