Nettyサーバーが受信した新しい接続は、ワーカースレッドプールにどのようにバインドされますか?

 

より技術的な共有が私をフォローできます

序文

オリジナル:ワーカースレッドプールにバインドされたNettyサーバーが受信した新しい接続はどのようになっていますか?

Nettyサーバーで新しい接続を検出するプロセスの以前の分析では、NioServerSocketChannelは新しい接続を読み取った後、サーバーチャネルにバインドされたpipeline.fireChannelRead()メソッドを周期的に呼び出し、各新しい接続をパラメーターとしてパッケージ化し、このメソッドをこれは、サーバーチャネルのパイプライン、つまりチャネルのハンドラチェーン上のフローを通過します。詳細のこの部分については、後で詳しく説明します。 

サーバー側の新しいチャネルに接続されたパイプラインのフローで、Nettyによって構成されたボススレッドプールとワーカースレッドプールがどのように連携するかを見てみましょう。

サーバーの新しい接続アクセサーのソースコード分析

前の記事を簡単に確認してください:Nettyは新しい接続アクセスイベントをどのように処理しますか?Nettyサーバーで新しい接続を検出するプロセスの分析で、NioMessageUnsafeクラスのread()メソッドのソースコードを呼び出します。

最後の赤いボックスを見ると、新しい接続はループのチャネルパイプラインに沿って渡されていますNioMessageUnsafeは、前述のNetty Channel-Unsafeサーバー実装クラスの内部インターフェイスです。

では、これらの新しい接続がその後配信されるとどうなりますか?これも重要な問題です。つまり、新しく接続されたNettyクライアントのチャネルが、カプセル化された後にNettyのI / Oスレッドにどのように関連付けられるかです。前述の新しい接続アクセサを見てみましょう。関連する機能は主にこのアクセサによって実現されます。

真実に戻るために、ServerBootstrapAcceptorのソースコードを見てください。これはChannelInboundHandlerAdapterを継承する内部クラスです(後でNettyのパイプラインメカニズムを説明します)。

次に、サーバーの起動プロセスを最初に確認します。サーバー起動の中心的な操作は、ポートをバインドすることです。つまり、ユーザーコードのserverBootstrap.bind(xx);メソッドで起動し、ServerBootstrapのdoBindメソッドを呼び出し、doBindメソッドでServerBootstrapのinitAndRegister()メソッドを呼び出します。以下に示すように、サーバーチャネルを初期化してI / Oマルチプレクサーを登録する方法:

このメソッドは、リフレクションによってサーバー側にNioServerSocketChannelを作成し、JDKとパイプラインなどの一部のコンポーネントを格納するServerSocketChannelを作成してから、チャンネルの初期化操作、つまりServerBootstrapのinit(チャンネル)メソッドを実行します(分析はサーバーコードなので、 ServerBootstrapクラスによるinitの実装を見てください)、initメソッドに新しい接続アクセサーを作成するロジックがあります。以下の赤いボックスに示すように、initでサーバーパイプラインを構成すると、デフォルトでServerBootstrapAcceptorハンドラーが追加されます。

最初にプロセス全体を完了します。

1.最初に、ServerBootstrapのinitメソッドは、サーバーチャネルのパイプラインにChannelInitializerを追加します。このクラスによって実装されるvoid initChannel(Channel ch)メソッドでは、ユーザーコードで構成されたサーバーハンドラーが最初に追加されます。サーバー側のハンドラー構成はめったに使用されず(つまり、ハンドラー()API)、最も一般的に使用されるのは、クライアントのハンドラーを構成することです。

2.次に、新しい接続アクセサーを非同期で追加します-ServerBootstrapAcceptor。具体的には、ServerBootstrapAcceptorをパイプラインに追加する操作がタスクにカプセル化され、サーバーに委任されたNIOスレッドが非同期で実行されます。新しい接続が到着すると、タスク実行されました。つまり、Nettyサーバーチャネルの最小パイプライン構造は次のとおりです。

ここでは、事前にNettyのインバウンドイベントとアウトバウンドイベントの概念に触れています。いわゆるインバウンドイベント、つまりインバウンドイベント、つまりNettyのNIOスレッドがアクティブに開始することは、ユーザービジネスハンドラーの操作、つまり受動的に開始されるイベントです。 fireXXXメソッドを介して拡散します。

たとえば、チャネル接続が成功した、チャネルが閉じた、チャネルに読み取るデータがある、チャネルに登録されたI / Oマルチプレクサが成功した、チャネルがI / Oマルチプレクサの登録を解除した、例外がスローされた、これらはパッシブ実行です。コールバックイベントとその処理には、特別なハンドラー実装である、統合された呼び出しインバウンドハンドラーがあります。逆に、送信イベントと送信ハンドラがあります。送信イベント-送信イベントは、ユーザースレッドまたはユーザーコードによって開始されるイベントです。送信イベントは次のとおりです。

たとえば、サーバーはポートをアクティブにバインドし、接続をアクティブに閉じ、クライアントはサーバーにアクティブに接続し、サーバー(クライアント)はメッセージをアクティブに書き込み、これらのイベントの特性はユーザーによって開始されます。これら2つのタイプのイベントでは、デフォルトでNettyによって提供されるハンドラーに加えて、ユーザーはインバウンド/アウトバウンドハンドラーをカスタマイズして、独自のインターセプトロジックを実装することもできます。これは、責任のチェーン(責任のチェーンとも呼ばれる)モデルの概念でもあります。

結局のところ、新しい接続を読み取るサーバーのプロセスを引き続き分析しますが、ここでは新しい接続アクセスを分析しているので、受信ハンドラーのみを調べます。まず、インバウンドイベントフローのシーケンスがパイプラインのヘッドノードから始まり、各インバウンドハンドラーノードを通過して、テールノードの最後に流れることを確認します。ここでは、Head-> ServerBootstrapAcceptor-> Tailです。次のように:

また、テールノードは本質的にインバウンドハンドラーであり、ヘッドノードは本質的にアウトバウンドハンドラーであることも知っておく必要があります。これらは後で詳細に解体されます。理由がわからなくても問題ありません。

前述のように、NioMessageUnsafeクラスのread()メソッドは、次のように、最終的に読み取りクライアントの新しい接続を渡します。

具体的には、各受信ハンドラーの後続のChannelReadイベントをトリガーします(channelReadは受信イベントと呼ばれます)。受信イベントはすべて、パイプラインのヘッドノードであるHeadContextから伝達され、このイベントがトリガーされて伝達されますこれは、pipeline.fireChannelRead(xxx)メソッドです。

:サーバは、以下のコードを開始したときに覚えていると、serverBootstrap.handler(new ServerHandler())serverBootstrap.childHandler(new ServerHandler());

そのときに私はこの結論を出しました:.handlerメソッドによって追加されたハンドラーは、サーバーの初期化時に追加されるサーバーチャネルのパイプラインに追加され、.childHandlerメソッドによって追加されたハンドラーは、クライアントチャネルのパイプラインに追加されます、新しい接続アクセスを処理するときに追加されます。理由がわかったので、ServerBootstrapがinitを呼び出すときは、最初にpipeline.addLast(ハンドラー)を呼び出してから、ServerBootstrapAccepterを追加します。これにより、サーバーパイプラインも次のようにこの構造体になる可能性があります(非常によく知られた構造)。

ここでは、2つの操作がサーバーとクライアントのパイプラインにそれぞれハンドラーを追加することを理解する必要があります。

serverBootStrapAccepter自体も着信ハンドラです。以前の分析によると、インバウンドイベントの伝播シーケンスは、head->ユーザー定義のインバウンドハンドラー-> ServerBootstrapAcceptor-> tailです。デモではサーバーのハンドラーを定義しなかったため、ServerBootstrapAcceptorのchannelReadメソッドを直接呼び出しました。アクセスデバイスの要点を検討する必要があるServerBootstrapAcceptorのchannelReadメソッドのソースコードは以下の通りです。

ServerBootstrapAcceptorは、ServerBootstrapの内部クラスです。デバッグプロセスを見てみましょう。起動すると、msgが強制的にChannelに送られます。つまり、ここで受け取ったmsg変数は、基本的に、Nettyによってカスタムチャネルとして読み込まれたばかりのクライアントの新しい接続です。後続のServerBootstrapAcceptorは主に3つのことを行いました。

1.黄色は上記の分析です。ユーザー設定のクライアントチャネルハンドラーをアクセサーに追加します。つまり、後で詳しく説明するサーバーコードの.childHandler()カスタムChannelHandlerを介してユーザーをクライアントのパイプラインに追加します。

2.黄色の2で、主にクライアントチャネルのchildOptionsとchildAttrsを設定するために、ユーザー構成オプションと属性を設定します。childOptionsは、チャネルの下部にあるTCPプロトコル用に構成された属性です。childAttrsは、チャネル自体の一部の属性です。その本質はマップです。たとえば、現在のチャネルの生存時間、キーなどを保存できます。

3.黄色の3で、ワーカースレッドプール内のNIOスレッドを選択し、コード内の子変数であるクライアントチャネルにバインドします。このステップは非同期操作であり、registerメソッドを介して実装されます。このメソッドは、サーバーの起動時にサーバーチャネルのI / Oマルチプレクサーを登録するためのコードロジックを再利用します。この最後のステップは、2つの小さなステップに分かれています。

  • ワーカースレッドプールは、NioEventLoopスレッドを選択して、EventLoopのスレッドセレクター-チューザーのnext()メソッドを介して新しい接続にバインドします。これには、サーバー側スレッドプールと同じロジックがあります。

  • クライアントの新しいチャネルをこのNioEventLoopのI / Oマルチプレクサに登録し、そのためのOP_READイベントを登録します。

以下の2つの小さなステップの詳細を分析します。デバッグを通じてレジスタを追跡し、MultithreadEventLoopGroupのregisterメソッドに到達しました。ソースコードは次のとおりです。

最後に、親クラスio / netty / util / concurrent / MultithreadEventExecutorGroupクラスを入力します。これは非常によく知られています。以前に分析したNioEventLoopGroupのスレッドセレクターを入力します。

ここで使用する最適化メソッドは、ビット操作でNioEventLoopスレッドを選択します。idxが0であることがわかります。つまり、workerGroupスレッドプール内のスレッドがこの時点で最初のスレッドを選択したところです。これは、現在実行しているサーバーが受信した最初のクライアント接続であるため、新しい接続になると、それが順番になります。次のスレッドを開始してバインドします。最後のスレッドにバインドされている場合、idxは再び0から始まり、ループします。NIOスレッドはまだ開始されていないことに注意してください。前述のように、Nettyは最適化されています。Nettyのスレッドプールは開始が遅れています。

MultithreadEventLoopGroupクラスのregisterメソッドでNioEventLoopスレッドを選択した後、next()メソッドはNioEventLoopインスタンスを返し、インスタンスのregisterメソッドの呼び出しを続行します。つまり、次のステップは、次のソースコードであるNioEventLoopの直接の親クラスSingleThreadEventLoopのregisterメソッドにジャンプします。 :

2番目のregisterメソッドに呼び出されると、内部のchannel()メソッドはクライアントのNioSocketChannelを返し、unsafe()メソッドはNioByteUnsafeインスタンスであり、最終的にクライアントチャネルのUnsafe registerメソッドを呼び出します。これは、AbstractChannel-AbstractUnsafeのregisterメソッドの内部クラスです。ソースコードは次のとおりです。

このメソッドのコードは非常によく知られているはずです。Nettyサーバーが起動する前に分析しました。つまり、クライアントの新しい接続にI / Oマルチプレクサーを登録するロジックが、このコードセットを再利用しました。 Nettyの優れた建築設計からも恩恵を受けました。

AbstractUnsafeのregisterメソッドを実行するロジックを分析してみましょう。

1.最初に現在のクライアントのI / Oスレッドとチャネルを確認し、次に黄色の1で現在のスレッドがNIOスレッドであるかどうかを確認します。これは明らかにfalseです。これは、現時点ではクライアントNIOスレッドが選択されていますが、 NIOスレッドが開始されていません。登録ロジック全体がまだユーザースレッドで実行されています。以下に示すように、私のデモはメインスレッドであるため、1はここで失敗し、次にコードを実行して実際の登録ロジックを開始したばかりに委任しますクライアントのNIOスレッドは非同期で実行されるため、スレッドの安全性も保証されます。

2.黄色の2つの場所を見てください。つまり、elseコードでは、以前に選択したNIOスレッドがNioEventLoopの実行メソッドによって開始されます(もちろん、すでに開始されている場合は、開始ステップはスキップされます)。 NIOスレッドを開始すると、Nettyのスレッドプールが遅延開始を達成したことも証明できます。

3.最後に、黄色の3つの場所を見て、次のようにregister0メソッドに入り、その実装ソースコードを調べました。

最も重要なメソッドはdoRegister()メソッドで、赤いボックスを見てください。このメソッドに入ると、サブクラスAbstractNioChannelにあることがわかりました。これはよく知られているか、または次のように、サーバー側でServerSocketChannelを登録するプロセスと同じです。

Nettyがカプセル化するのは、JDKに登録されたChannel Selectorのロジックです。このメソッドでは、クライアントのチャネルをクライアントのNioEventLoopスレッドのI / Oマルチプレクサーに登録し、NioSocketChannelオブジェクトをJDKチャネルにアタッチしますが、この時点で登録されている対象のI / Oイベントはまだ0ですが、それは何ですか?注意はありません。つまり、クライアントチャネルはまだ初期化状態にあり、実際の登録I / Oイベントはまだバックプロセスにあります。

このメソッドは登録ループを無限ループで書き込むことに注意してください。この使用法を学習する目的は、特定の例外が発生した場合でも、物事を完了する必要があることを確認することです。

register0メソッドに戻り、もう一度見てください。登録が完了すると、中断状態のhandlerAddedイベントが最初にトリガーされます。つまり、黄色の1のコードが最初に実行されます。これは、クライアントの新しい接続用のユーザー定義のクライアントハンドラーの追加に相当します。論理。次に、黄色の2が実行され、現在のチャネルが正常に登録されたというイベントがトリガーおよび伝播されます。現在のチャネルがまだ生きている場合、それは3か所でコードを実行し続けます。つまり、最初の登録された新しいチャネルに対して、チャネル接続の成功(アクティブ状態)のイベントが伝播されます。

最後に、現在のチャネルが初めて登録されていない場合は、自動読み取りメッセージを構成するかどうかを決定します(Nettyのデフォルトは読み取り優先度です)。構成されている場合は、黄色の4のコードが実行され、その後の詳細な説明が表示されます。

小さな結び目

新しい接続にNIOスレッドを割り当て、新しい接続にI / Oマルチプレクサーを登録するコアは、ServerBootstrapAcceptorを理解し、サーバーチャネルの最小パイプライン構成を知ることです。Head-> ServerBootstrapAcceptor-> Tail

理解ServerBootstrapAcceptor:

1. childHandlerを追加するまでの遅延-新しく接続されたパイプラインにカスタムChannelHandlerを追加します。現在のチャネルがI / Oマルチプレクサーを登録した後で追加する必要があります。

2.オプションとattrs-set childOptionsとchildAttrsを設定します

3. NioEventLoopを選択してセレクタに登録します。コアは、ワーカースレッドプールのセレクタのnext()メソッドを呼び出してNioEventLoopを選択します。doRegister()メソッドを使用して、ワーカースレッドにバインドされたセレクタへの新しい接続を登録します。ここでの新しい接続とセレクターは、多対1の関係にあります。

ようこそ注意

dashuaiのブログは生涯学習の実践者であり、大規模なプログラマーであり、仕事の経験、研究ノートの共有、インターネット業界を含むがこれらに限定されない毎日の嘔吐、および一部のPDF電子書籍、資料、および宣伝、歓迎の支援に焦点を当てています白珠!

おすすめ

転載: www.cnblogs.com/kubixuesheng/p/12723456.html