記事ディレクトリ
1.Nettyコンポーネントアーキテクチャ
次のワークフロープロセスに示す、Reactorマルチスレッド実装のNettyフレームベースのイベント駆動型マスター。さまざまな機能に応じて、Nettyのコンポーネントは2つの部分に分けることができます。イベント配信コンポーネントとビジネス処理コンポーネント
2.イベント配布コンポーネント
メインイベント配信成分が含まEventLoopGroup
やAcceptor
カテゴリ、EventLoopGroup
イベント配信処理を担当するイベントループ基であるが、Acceptor
プロセッサ・サーバ・プロセスから、イベントループ群から新しい接続を設定するためのメインイベントループの登録を担当する、イベントを受け入れます接続の役割に
2.1イベントループグループEventLoopGroup
EventLoopGroup
メインEventLoopのライフサイクルを管理し、一連のEventLoopを内部的に維持し、各EventLoopは複数のチャネルでのイベントの処理を担当し、1つのチャネルは1つのEventLoopのみに対応します。
2.1.1イベントループグループのNioEventLoopGroupインスタンス
NioEventLoopGroup
継承構造は非常に深く、各キー属性は異なる親クラスに分散されています。より重要なものは次のとおりです。
MultithreadEventLoopGroup:
DEFAULT_EVENT_LOOP_THREADS
:イベントループスレッドのデフォルト数を示します。デフォルト値はマシンで使用可能なコアの数* 2MultithreadEventExecutorGroup:
children
:EventExecutor配列。これは、NioEventLoopインスタンスオブジェクトである現在のグループに属するイベントループスレッドを維持します。
MultithreadEventExecutorGroup
されEventLoopGroup
、その主なコンストラクタを実現するために最も重要なことは、次のことを完了しました。
new ThreadPerTaskExecutor(new DefaultThreadFactory())
Executorインスタンスを生成し、そのスレッドファクトリを指定しますnewChild()
現在のグループのメソッドNioEventLoopの新しいインスタンスを呼び出しThreadPerTaskExecutor
、後続のスレッドEventLoopの作成と開始に使用されるExecutorオブジェクトのパラメーターを指定します- 新しい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タスクの実行時間と等しいことを示します
I/O 任务
NioEventLoop#processSelectedKeys()
トリガーメソッドによって、accept、connect、read、writeなどのイベントで準備ができているselectionKey非 IO 任务
register0、bind0、その他のタスクなど、タスクキュー内のタスクに追加されたNioEventLoop#runAllTasks()
トリガーメソッド
NioEventLoop
の継承構造も非常に深く、その主要な属性は次のとおりです。
selector
セレクター、登録されているチャネル接続のさまざまなイベントを監視するために使用されますSingleThreadEventExecutor:
taskQueue
:タスクが保存されるキュー
executor
:新しいスレッドのスレッドファクトリとエグゼキュータ
NioEventLoop#run()
このメソッドはイベントループ処理のコアロジックであり、このメソッドはイベントループSingleThreadEventExecutor#execute()
の開始への入り口です。ここではこれ以上の分析は行いません。
2.2アクセプターに接続する
NettyはAcceptor
、业务处理组件
その実装クラスがServerBootstrapAcceptor
継承するためChannelHandler
、主にチャネルサーバー側の処理の完了を担当するため、所属を実現しています。
このコンポーネントがイベント配信コンポーネントとして分類される理由は、機能部門観点からは、MainReactorとSubReactorの間の接続構造に属し、SubReactorへのクライアント要求を受信するMainReactorによって確立された新しい接続を登録する責任があります。
ServerBootstrapAcceptor#channelRead()
これは、Acceptor
SubReactorへの新しいレジスタ接続のロジックを実現するための最も重要な方法です。
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
およびが含まれますChannelHandlerContext
。AChannel
にはChannelPipeline
しばらくが含まれ、二重にリンクされたリストChannelPipeline
も保持さChannelHandlerContext
れます。各リストChannelHandlerContext
は、ChannelHandler
ChannelHandler
インターフェイスクラス継承の異なる実装によれば、それを分割してChannelInboundHandler入站处理器
、ChannelOutboundHandler出站处理器
それぞれ読み取り操作と書き込み操作の両方を行うことができます。入站事件(read)
そして、出站事件(write)
でChannelPipeline
チェーン処理、イベント配信を終了するインターセプターがない場合、インバウンドイベントはリンクリストヘッドから最後のインバウンドプロセッサテールに戻され、アウトバウンドイベントはリンクリストテールから最初のアウトバウンドプロセッサヘッドに渡されます。2つのタイプがあります。 ChannelHandlerは互いに干渉しません
3.1チャンネル
Channel
パイプラインです、バイトバッファをもう一方の端のエンティティに接続するために使用されますこれは、に分けることができます服务端 NioServerSocketChannel
し、客户端 NioSocketChannel
2つの広いカテゴリー。Nettyでは、複数のプロセッサChannel
を介しChannelPipeline
てChannelHandler
、IOデータの処理が最終的に完了します。
- サーバーNioServerSocketChannel
クラスはAbstractNioMessageChannel
、ChanelIONioMessageUnsafe
操作を完了するためのターゲットの保持を継承します- クライアントのNioSocketChannel
クラスは、チャネルイベントのプロセスを完了するAbstractNioByteChannel
ためにNioByteUnsafe
ターゲットの保持を継承します継承されたすべての
AbstractChannel
チャネルインスタンスはDefaultChannelPipeline
、IOデータストリーム処理を完了するためのオブジェクトを保持し、もう一方は、EventLoop
チャネルが属するイベントループインスタンスオブジェクト識別子を保持します。これを使用して、ジョブの送信を処理できます。
3.2 ChannelHandler
ChannelHandler
I / Oイベントを処理したりI / O操作をインターセプトしたりするためのインターフェイスである場合、イベントはChannelPipeline
次のサービス処理チェーンに転送され、ChannelHandler
終了イベントまたは配信を処理できます。
ChannelHandler
主に分割进站处理器ChannelInboundHandler
し、出站处理器ChannelOutboundHandler
それぞれ読み出しおよび書き込み動作方法。ユーザーが処理ロジックプロセッサを作成します。これは内部NettyAbstractChannelHandlerContext
オブジェクトにパッケージ化され、サービス処理チェーンに登録されて、データIOのロジック処理を完了します。
3.3 ChannelHandlerContext
ChannelHandlerContext
保存Channel
主に3つの実現の種類があり、関連するコンテキスト情報、DefaultChannelHandlerContext
、HeadContext
だけでなく、TailContext
次のキー属性の、:
AbstractChannelHandlerContext:
next
:
prev
現在のパイプラインプロセッサコンテキストノードの次のノード:現在のパイプラインプロセッサコンテキストノードの前のノード:
inbound
現在のパイプラインプロセッサコンテキストノードのインバウンドおよびアウトバウンド識別属性trueの場合、ノードはインバウンドイベントを処理できます
outbound
:現在のパイプラインプロセッサコンテキストノードのアクセスステーション識別属性がtrueの場合、ノードがアウトバウンドイベントを処理できることを示します
pipeline
。ChannelPipeline
例:DefaultChannelPipeline
クラスとして実装DefaultChannelHandlerContext:
handler
:カプセル化されたChannelHandlerハンドラー
ChannelPipeline
サブクラスDefaultChannelPipeline
は、オブジェクトがインスタンス化さHeadContext
れTailContext
、オブジェクト、およびフロントとリアが相互にポインタを合わせたときに生成され、二重にリンクされたリストを形成します。DefaultChannelPipeline#addLast()
このメソッドは、二重リンクリストにノードを追加します。主な処理手順は次のとおりです。
newContext()
ChannelHandler
カプセル化DefaultChannelHandlerContext
するオブジェクトaddLast0()
新しいChannelHandlerContext
オブジェクトが二重リンクリストに追加されます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;
}