Nettyソースコード分析(1)-コアコンポーネントとアーキテクチャ

1.Nettyコンポーネントアーキテクチャ

次のワークフロープロセスに示す、Reactorマルチスレッド実装のNettyフレームベースのイベント駆動型マスターさまざまな機能に応じて、Nettyのコンポーネントは2つの部分に分けることができます。イベント配信コンポーネントとビジネス処理コンポーネント

ここに写真の説明を挿入

2.イベント配布コンポーネント

メインイベント配信成分が含まEventLoopGroupAcceptorカテゴリ、EventLoopGroupイベント配信処理を担当するイベントループ基であるが、Acceptorプロセッサ・サーバ・プロセスから、イベントループ群から新しい接続を設定するためのメインイベントループの登録を担当する、イベントを受け入れます接続の役割に

2.1イベントループグループEventLoopGroup

EventLoopGroup メインEventLoopのライフサイクルを管理し、一連のEventLoopを内部的に維持し、各EventLoopは複数のチャネルでのイベントの処理を担当し、1つのチャネルは1つのEventLoopのみに対応します。

2.1.1イベントループグループのNioEventLoopGroupインスタンス

NioEventLoopGroup 継承構造は非常に深く、各キー属性は異なる親クラスに分散されています。より重要なものは次のとおりです。

  1. MultithreadEventLoopGroup:
    DEFAULT_EVENT_LOOP_THREADS :イベントループスレッドのデフォルト数を示します。デフォルト値はマシンで使用可能なコアの数* 2
  2. MultithreadEventExecutorGroup:
    children:EventExecutor配列。これは、NioEventLoopインスタンスオブジェクトである現在のグループに属するイベントループスレッドを維持します。

MultithreadEventExecutorGroupされEventLoopGroup、その主なコンストラクタを実現するために最も重要なことは、次のことを完了しました。

  1. new ThreadPerTaskExecutor(new DefaultThreadFactory()) Executorインスタンスを生成し、そのスレッドファクトリを指定します
  2. newChild()現在のグループのメソッドNioEventLoopの新しいインスタンスを呼び出しThreadPerTaskExecutor、後続のスレッドEventLoopの作成と開始に使用されるExecutorオブジェクトのパラメーターを指定します
  3. 新しいNioEventLoopの例に失敗した場合、各NioEventLoopインスタンスを呼び出すとshutdownGracefully()、イベントループスレッドを開始する方法が作成されます。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    
    
        if (nThreads <= 0) {
    
    
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (executor == null) {
    
    
            // #1
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
    
    
            boolean success = false;
            try {
    
    
                // #2
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
    
    
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
    
    
                if (!success) {
    
    
                    for (int j = 0; j < i; j ++) {
    
    
                        // #3
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
    
    
                        EventExecutor e = children[j];
                        try {
    
    
                            while (!e.isTerminated()) {
    
    
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
    
    
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        chooser = chooserFactory.newChooser(children);

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
    
    
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
    
    
                if (terminatedChildren.incrementAndGet() == children.length) {
    
    
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
    
    
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

2.1.2イベントループインスタンスNioEventLoop

NioEventLoop非同期タスクの送信、メインの実行I/O 任务、およびをサポートするシングルスレッドスレッドプールと同様に、スレッドとタスクのキューを維持します非 I/O 任务変数ioRatioコントロールよりも両方のタスクの実行時間。デフォルトは50、非IOタスクの実行時間がIOタスクの実行時間と等しいことを示します

  1. I/O 任务
    NioEventLoop#processSelectedKeys()トリガーメソッドによって、accept、connect、read、writeなどのイベントで準備ができているselectionKey
  2. 非 IO 任务
    register0、bind0、その他のタスクなど、タスクキュー内のタスクに追加されたNioEventLoop#runAllTasks()トリガーメソッド

NioEventLoop の継承構造も非常に深く、その主要な属性は次のとおりです。

  1. selector セレクター、登録されているチャネル接続のさまざまなイベントを監視するために使用されます
  2. SingleThreadEventExecutor:
    taskQueue:タスクが保存されるキュー
    executor:新しいスレッドのスレッドファクトリとエグゼキュータ

NioEventLoop#run()このメソッドはイベントループ処理のコアロジックであり、このメソッドはイベントループSingleThreadEventExecutor#execute()の開始への入り口です。ここではこれ以上の分析は行いません。

ここに写真の説明を挿入

2.2アクセプターに接続する

NettyはAcceptor业务处理组件その実装クラスがServerBootstrapAcceptor継承するためChannelHandler、主にチャネルサーバー側の処理の完了を担当するため、所属を実現しています。

このコンポーネントがイベント配信コンポーネントとして分類される理由は、機能部門観点からは、MainReactorとSubReactorの間の接続構造に属し、SubReactorへのクライアント要求を受信するMainReactorによって確立された新しい接続を登録する責任があります。

ServerBootstrapAcceptor#channelRead()これは、AcceptorSubReactorへの新しいレジスタ接続のロジックを実現するための最も重要な方法です。

       public void channelRead(ChannelHandlerContext ctx, Object msg) {
    
    
            final Channel child = (Channel) msg;

            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);

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

            try {
    
    
                childGroup.register(child).addListener(new ChannelFutureListener() {
    
    
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
    
    
                        if (!future.isSuccess()) {
    
    
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
    
    
                forceClose(child, t);
            }
        }

ここに写真の説明を挿入

3.ビジネス処理コンポーネント

ビジネス処理コンポーネントにはChannel主に、、ChannelHandlerおよびが含まれますChannelHandlerContextAChannelにはChannelPipelineしばらくが含まれ、二重にリンクされたリストChannelPipelineも保持さChannelHandlerContextれます。各リストChannelHandlerContextは、ChannelHandler

ChannelHandlerインターフェイスクラス継承の異なる実装によれば、それを分割してChannelInboundHandler入站处理器ChannelOutboundHandler出站处理器それぞれ読み取り操作と書き込み操作の両方を行うことができます入站事件(read)そして、出站事件(write)ChannelPipelineチェーン処理、イベント配信を終了するインターセプターがない場合、インバウンドイベントはリンクリストヘッドから最後のインバウンドプロセッサテールに戻され、アウトバウンドイベントはリンクリストテールから最初のアウトバウンドプロセッサヘッドに渡されます。2つのタイプがあります。 ChannelHandlerは互いに干渉しません

ここに写真の説明を挿入

3.1チャンネル

Channelパイプラインです、バイトバッファをもう一方の端のエンティティに接続するために使用されますこれは、に分けることができます服务端 NioServerSocketChannelし、客户端 NioSocketChannel2つの広いカテゴリー。Nettyでは、複数のプロセッサChannelを介しChannelPipelineChannelHandler、IOデータの処理が最終的に完了します。

  • サーバーNioServerSocketChannel
    クラスはAbstractNioMessageChannelChanelIONioMessageUnsafe操作を完了するためのターゲットの保持を継承します
  • クライアントのNioSocketChannel
    クラスは、チャネルイベントのプロセスを完了するAbstractNioByteChannelためにNioByteUnsafeターゲットの保持を継承します

継承されたすべてのAbstractChannelチャネルインスタンスはDefaultChannelPipeline、IOデータストリーム処理を完了するためのオブジェクトを保持し、もう一方は、EventLoopチャネルが属するイベントループインスタンスオブジェクト識別子を保持します。これを使用して、ジョブの送信を処理できます。

ここに写真の説明を挿入

3.2 ChannelHandler

ChannelHandlerI / Oイベントを処理したりI / O操作をインターセプトしたりするためのインターフェイスである場合、イベントはChannelPipeline次のサービス処理チェーンに転送されChannelHandler終了イベントまたは配信を処理できます。

ChannelHandler主に分割进站处理器ChannelInboundHandlerし、出站处理器ChannelOutboundHandlerそれぞれ読み出しおよび書き込み動作方法。ユーザーが処理ロジックプロセッサを作成します。これは内部NettyAbstractChannelHandlerContextオブジェクトにパッケージ化され、サービス処理チェーンに登録されて、データIOのロジック処理を完了します。

ここに写真の説明を挿入

3.3 ChannelHandlerContext

ChannelHandlerContext保存Channel主に3つの実現の種類があり、関連するコンテキスト情報、DefaultChannelHandlerContextHeadContextだけでなく、TailContext次のキー属性の、:

  1. AbstractChannelHandlerContext:
    next
    prev現在のパイプラインプロセッサコンテキストノードの次のノード:現在のパイプラインプロセッサコンテキストノードの前のノード:
    inbound現在のパイプラインプロセッサコンテキストノードのインバウンドおよびアウトバウンド識別属性trueの場合、ノードはインバウンドイベントを処理できます
    outbound:現在のパイプラインプロセッサコンテキストノードのアクセスステーション識別属性がtrueの場合、ノードがアウトバウンドイベントを処理できることを示します
    pipelineChannelPipelineDefaultChannelPipelineクラスとして実装
  2. DefaultChannelHandlerContext:
    handler:カプセル化されたChannelHandlerハンドラー

ChannelPipelineサブクラスDefaultChannelPipelineは、オブジェクトがインスタンス化さHeadContextTailContext、オブジェクト、およびフロントとリアが相互にポインタを合わせたときに生成され、二重にリンクされたリストを形成します。DefaultChannelPipeline#addLast()このメソッドは、二重リンクリストにノードを追加します。主な処理手順は次のとおりです。

  1. newContext()ChannelHandlerカプセル化DefaultChannelHandlerContextするオブジェクト
  2. addLast0()新しいChannelHandlerContextオブジェクトが二重リンクリストに追加されます
  3. callHandlerAdded0()ChannelHandler#handlerAdded()プロセッサが処理チェーンに追加されたときに、書き込まれた関連ロジックが実行されるようにするコールバックメソッド
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    
    
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
    
    
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
    
    
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
    
    
                newCtx.setAddPending();
                executor.execute(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

ここに写真の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_45505313/article/details/106789671