NettyソースのBootstrap

9241 ワード

BootstrapクラスおよびServer Bootstrapクラスは、AbstractBootstrapから継承されます.
この抽象類は面白いので見てください

public abstract class AbstractBootstrap<B extends AbstractBootstrap<?>> {
...
}

汎用のクラスは自分のサブクラスです.実はこの類の役割が分かれば、なぜそう書いたのかがわかります.
参照doc:
AbstractBootstrap is a helper class that makes it easy to bootstrap a  Channel. It support method-chaining to provide an easy way to configure the AbstractBootstrap
つまり、これはヘルプクラスであり、チャネルの有効化を容易にするためにmethod-chainningをサポートしています.つまり、関数は自身への参照を返します.これで私たちは
instance.get().put(3).add(4)のような書き方は、NIOではよく見られます.
次にAbstractBootstrapに何があるか見てみましょう.
まずメンバー変数を見て、

public abstract class AbstractBootstrap<B extends AbstractBootstrap<?>> {
    private EventLoopGroup group;
    private ChannelFactory factory;
    private SocketAddress localAddress;
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private ChannelHandler handler;
}

イベントグループ.Nettyはイベント駆動で、イベントグループは様々なイベントの容器のはずです(まだ見ていないので、しばらく気にしなくてもいいと思います)
チャンネル工場です.これはもちろんチャンネルを生産するために使われています(ここにはいろいろなパイプがあるはずです.例えば、サービス側とクライアントの違いなど).
SocketAddressはローカルアドレスです
optionsはこのパイプのプロパティの集合です
attrsは属性キーの集合です(これが分からないので、先に置いてください)
ChannelHandlerはパイプの様々なイベントを処理するために使われているでしょう.
推測はここまで.具体的な機能関数を見てみましょう.
1, public B group(EventLoopGroup group)
この関数は簡単でgroupを設定し、入力パラメータが空である場合、または元のgroupが空でない場合、例外を放出することに注意します.そして、自分自身に戻り、汎用Bに注意
2, public B channel(Class channelClass)
このクラスはチャネルインスタンスを作成するために使用され、チャネルファクトリが使用されます.ファクトリ・モードを使用して、特定のインスタンスのサブクラスへの作成を延期します.ここでは工場の設置と見ることができます
3, public B channelFactory(ChannelFactory factory)
2は3を呼び出し、
4,public B localAddress(SocketAddress localAddress)
ローカルアドレスのバインド
5, public B localAddress(int port)
ローカルアドレスのバインド
6, public B localAddress(String host, int port)
同じ4,5
7, public  B localAddress(InetAddress host, int port)
通4,5,6
8, public B option(ChannelOption option, T value)
チャンネルをインスタンス化するときのパラメータ.valueがnullの場合、このoptionはremoveによって削除されます.
9, public B attr(Attribute key, T value)
Channelをインスタンス化した後のChannelの属性valueがnullの場合、attrはremoveされます.
10, public void shutdown()
ここで閉鎖されているのはEventLoopGroupです
11, protected void validate()
ここではprotectedになり、同類のパラメータ検証に使用されます.サブクラスを複写する場合はsuperを使用して親を呼び出す必要があります
12,protected final void validate(ChannelFuture future)
futureがnullでないことを検証し、11を呼び出し、複写できません.
13,public ChannelFuture bind()
チャンネルを作成してバインド
14,public B handler(ChannelHandler handler)
handler nullにhandlerを設定することはできません
15,public static boolean ensureOpen(ChannelFuture future)
futureのchannelがopen状態であることを確認します.そうしないとfutrueが失敗したChannelExceptionに設定されます.
16,public abstract ChannelFuture bind(ChannelFuture future)
チャンネルを所定のチャンネルファクトリーにバインドします.抽象的な方法は、具体的なチャンネルが必要であることを意味します.
その後の方法はgetメソッドに似ています.一つ一つ列挙しない.
以上から,このクラスは構成クラスである.
具体的なサブクラスを見てみましょう.
まず
BootstrapクラスはInternalLoggerとSocketAddress(remoteAddress)を追加しました
抽象クラスに実装されていないbind関数も,ここで実現した.

  @Override
    public ChannelFuture bind(ChannelFuture future) {
        validate(future);
        if (localAddress() == null) {
            throw new IllegalStateException("localAddress not set");
        }

        try {
            init(future.channel());
        } catch (Throwable t) {
            future.setFailure(t);
            return future;
        }

        if (!ensureOpen(future)) {
            return future;
        }

        return future.channel().bind(localAddress(), future).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    }

このコードは基本的に元のnettyのクライアント使用基準です.
まず、validateはfutrue、factory、groupを検証し、localAddressを検証します.
次にfutureの特性を利用してchannelを初期化し、channelが開いていない場合、ensureOpenはすでにfutureを設定した失敗であり、直接futureに戻り、channelが正常に開いた場合、ローカルアドレスをバインドし、失敗して閉じたChannelFutrueListenerを追加する.
ここで重要なのはinit(Channel)関数です.
Init関数は、まずチャネルがアクティブかどうか、登録されているかどうか、開いているかどうかを確認します.
次に、チャネルのpipelineにhandlerを追加し、チャネルOptionパラメータとAttributeプロパティを追加します.
チャンネルはもうNIOのチャンネルではありません.彼女はnettyのチャンネルです.どのチャンネルにもチャンネルPipelineがあり、チャンネルコンフィグのようなものもあります.これらを覚えておいて、私たちは後で彼らがどのように働いているかを見に行きます.
最後にgroup().register(channel).syncUninterruptibly();
これにより、私たちのchannelのregisterは同期を中断できないようになりました.ここの登録は私たちのChannelをEventLoopGroupの会員にするべきです.
bindに似た関数がconnectです.クライアントの1つの動作は、サービス側を接続することです.
ではconnectもbindと似ていますが、bindはbindローカルアドレスで、connectはリモートアドレスが必要なので、チェックするときは違いますし、connectのときはローカルバインドがなくてもいいですよ.
futureのようにchannel().connect(remoteAddress,futrue)
同様にfutureにリスナーを設定し、接続に失敗した場合にチャンネルを閉じる
クライアントのvalidateには、handlerのvalidateが1つ増えています.IllegalStateExceptionが報告されない場合は、handlerも設定します.
最後に、public Bootstrap channel(ClasschannelClass)を見てみましょう.
を選択します.
ここで、channelClassがAioSocketChannelであれば、AioSocketChannelFactoryを使用します.そうでなければデフォルトです.
ここにはAioSocketChannelFactoryも内蔵されており、実現は簡単です

 private final class AioSocketChannelFactory implements ChannelFactory {
        @Override
        public Channel newChannel() {
            return new AioSocketChannel((AioEventLoopGroup) group());
        }

        @Override
        public String toString() {
            return AioSocketChannel.class.getSimpleName() + ".class";
        }
    }

したがって、チャネルのパラメータがAioSocketChannelの場合、非同期チャネルが作成されることがわかります.
クライアント側がBootstrapを使用しているのを見て、Server Bootstrapを見てみましょう.
server側には、InetSocketAddressの具体的なIPアドレスが1つ増えています.同時に、次のプロセッサを受け入れます.

private final ChannelHandler acceptor = new ChannelInitializer<Channel>(){
    @Override
    public void initChannel(Channel ch) throws Exception {
        ch.pipeline().addLast(new Acceptor());
    }
};

同時に、サービス側にはChannelOptionとAttributeKeyのセットが追加され、対応するEventLoopGroupとChannelHandlerが追加され、これらの前には「child」があります.
関数public Server Bootstrapグループ(EventLoopGroup parentGroup,EventLoopGroup childGroup)を参照してください.
実はここのchildはclient端であり、parentはacceptorであることがわかります.
channel(ClasschannelClass)関数には、channelClassがServerChannelであるかどうかのサブクラスが1つ追加されています.次に、チャンネルクラスが非同期チャンネルかどうか、そうであればAioServerSocketChannelaFactoryを設定します.
ServerBootstrapでは、childのパラメータオプションとプロパティを設定することもできます.
ServerBootstrapにとって重要なのはAcceptorです.このクラスは接続されたChannelを処理しています.

rivate class Acceptor
            extends ChannelInboundHandlerAdapter implements ChannelInboundMessageHandler<Channel> {

        @Override
        public MessageBuf<Channel> newInboundBuffer(ChannelHandlerContext ctx) throws Exception {
            return Unpooled.messageBuffer();
        }

        @Override
        public void freeInboundBuffer(ChannelHandlerContext ctx, ChannelBuf buf) throws Exception {
            // Nothing to free
        }

        @SuppressWarnings("unchecked")
        @Override
        public void inboundBufferUpdated(ChannelHandlerContext ctx) {
            MessageBuf<Channel> in = ctx.inboundMessageBuffer();
            for (;;) {
                Channel child = in.poll();
                if (child == null) {
                    break;
                }

                child.pipeline().addLast(childHandler);

                for (Entry<ChannelOption<?>, Object> e: childOptions.entrySet()) {
                    try {
                        if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
                            logger.warn("Unknown channel option: " + e);
                        }
                    } catch (Throwable t) {
                        logger.warn("Failed to set a channel option: " + child, t);
                    }
                }

                for (Entry<AttributeKey<?>, Object> e: childAttrs.entrySet()) {
                    child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
                }

                try {
                    childGroup.register(child);
                } catch (Throwable t) {
                    child.unsafe().closeForcibly();
                    logger.warn("Failed to register an accepted channel: " + child, t);
                }
            }
        }
    }


クライアントチャネルを取得しchildの変数を用いて設定することが容易に理解できる.
これで、Bootstrapの機能を大体理解しました.同時にnettyの使い方も少し見えてきました.
その後は藤に沿って瓜を触ることができ、nettyの様々な実現を垣間見ることができます.