NioEventLoop作成
NioEventLoopは、その主な責任は、そのサービスのチャンネルに登録して、新しい接続を見つけ、読み取りおよび書き込みこれらのチャネル上のI / Oイベントを発生し、その後、チャネルパイプライン処理にケースを報告することで、網状の重要なコンポーネントです。ネッティーを使用する場合は、行うには、私たち最初の必要性は、スレッドプールのスレッドに類似NioEventLoopのコレクションですNioEventLoopGroupを作成することです。一般的に、サーバはworkerGroup呼ばbossGroupと呼ばれる2つのグループを、作成されます。初期化プロセス以降のIOがworkerGroupイベントを言及した後、要求を受け入れ、新しい接続を作成するためにバインドされたポートリスニングの責任bossGroup。
そして、図クラスNioEventLoop NioEventLoopGroup
まず、NioEventLoopとNioEventLoopGroupのクラス図を見て
マルチクラスが、混乱、それは三つの特徴を見つけることができます:
- どちらも、スレッドプールとの接触を確立するように、ExecutorServiceのを継承します
- NioEventLoop継承SingleThread、NioEventLoopは、マルチスレッドを継承しています
- NioEventLoopまた、これは、スレッドプールに関連するタスクのスケジューリングとタイミングだと思いますし、難しいことではありませんAbstractScheduledEventExecutorを継承しました
NioEventLoopGroup作成
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
我々はコンストラクタbossGroupとworkerGroupを見てみましょう。
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
除此之外,还有多达8种构造方法,这些构造方法可以指定5种参数:
1、最大线程数量。如果指定为0,那么Netty会将线程数量设置为CPU逻辑处理器数量的2倍
2、线程工厂。要求线程工厂类必须实现java.util.concurrent.ThreadFactory接口。如果没有指定线程工厂,那么默认DefaultThreadFactory。
3、SelectorProvider。如果没有指定SelectorProvider,那么默认的SelectorProvider为SelectorProvider.provider()。
4、SelectStrategyFactory。如果没有指定则默认为DefaultSelectStrategyFactory.INSTANCE
5、RejectedExecutionHandler。拒绝策略处理类,如果这个EventLoopGroup已被关闭,那么之后提交的Runnable任务会默认调用RejectedExecutionHandler的reject方法进行处理。如果没有指定,则默认调用拒绝策略。
最後に、親クラスのコンストラクタMultiThreadEventExecutorGroupにNioEventLoopGroupのリロードは、ここでは、コードのいくつかの堅牢性を省きます。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {
// 步骤1
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 步骤2
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
children[i] = newChild(executor, args);
}
// 步骤3
chooser = chooserFactory.newChooser(children);
// 步骤4
final FutureListener<Object> terminationListener = future -> {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
// 步骤5
Set<EventExecutor> childrenSet = new LinkedHashSet<>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
ここでは5つのステップに分けることができ、ステップバイステップで説明
ステップ1
最初のステップは、スレッドプールのexecutorを作成することです。workerGroupコンストラクタからデフォルトエグゼキュータに渡され、見られるので、最初のexecutorを作成し、nullです。アクションnewDefaultThreadFactoryプレフィックスが配置されたスレッドとスレッドの優先順位で、デフォルトの接頭辞の命名規則はnioEventLoopGroup-XYで、スレッドの優先順位が中間位置に、5です。
ThreadPerTaskExecutorに、newDefaultThreadFactoryを作成した後。それは単に彼らのメンバ変数に割り当てられた工場の施工方法から見ることができ、jucパッケージインターフェーストップレベルのスレッドプールを実装しています。そして、それはインターフェースのメソッド呼び出しthreadFactoryのnewThreadメソッドを実装します。名前が示すように、それはスレッドを作成し、スレッドがすぐに開始します。
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
我々はDefaultThreadFactoryを振り返っのでnewThreadの方法は、彼がFastThreadLocalThreadを作成しました。これは、名前が示すように、ネッティーカスタム・スレッド・クラスであり、ネッティーその高速なパフォーマンスを考えます。後にその決意を残しました。ここでは、スレッド・プールを作成するために、ステップ1で完了です。全体的に彼と私たちは通常、スレッドプールは同じではありません使用し、スレッドを開始するスレッドプールのスレッドやタスクキューが、タスクを設定しないでください。(質問:でしょうが、それは直接スレッド爆発することを複数のタスクではありません?)
@Override
public Thread newThread(Runnable r) {
Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
return t;
}
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
}
ステップ2
ステップ2 NioEventLoopにworkerGroupを作成することです。サンプルコードでは、渡されたスレッド数が0である、明らかに実際には0 nioEventLoopスレッドを作成しません。MultithreadEventLoopGroupは、親クラスのコンストラクタを呼び出すと、スレッドの数が決定され、それが0であれば、デフォルトのスレッド数が渡されると、デフォルト値は、CPUコア数の二倍です
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
// 静态代码块初始化DEFAULT_EVENT_LOOP_THREADS
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
}
次は、newChildノード法により各EventExecutorための対応NioEventLoopを作成することです。NioEventLoopGroupは、パラメータを作成する場合、このメソッドはNioEventLoopへの引数の一部に導入された、最初の3つが渡されています。デフォルト値は上記参照します
- JavaのNIOセレクターオブジェクトを作成するためのSlectorProvider.provider、。
- SelectStrategyFactory、植物の選択ポリシー。
- RejectedExecutionHandlers、ごみ実行プロセッサ。
- EventLoopTaskQueueFactory、タスクキューの工場出荷時のデフォルト値はnullです。
次のように、NioEventLoopコンストラクタを入力します:
NioEventLoop构造函数
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory queueFactory) {
super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
// 父类构造函数
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, Queue<Runnable> taskQueue,
RejectedExecutionHandler rejectedHandler) {
super(parent);
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
this.executor = ThreadExecutorMap.apply(executor, this);
this.taskQueue = ObjectUtil.checkNotNull(taskQueue, taskQueue");
rejectedExecutionHandler = ObjectUtil.checkNotNullrejectedHandler, "rejectedHandler");
}
NewTaskQueueは、最初のタスクキューを作成するためのメソッドを呼び出します。これは、消費者のロックフリーキューの1つのプロデューサ以上であるMPSCです。ときに彼らの親に親クラスのコンストラクタ、親クラスのコンストラクタ、NioEventLoopGroupを呼び出した後、およびThreadPerTaskExecutorによって実行などのタスクエグゼキュータは----匿名内部クラス経由で渡された、との実装で作成されました現在のスレッドとNioEventLoop結合。他の特性はまた、一度にすべてを設定されています。
nioEventLoopコンストラクタでは、我々はネッティーそのパッケージを見てみたいことがあり、それはセレクタを作成しました。
unwrappedSelector = provider.openSelector();
if (DISABLE_KEY_SET_OPTIMIZATION) {
return new SelectorTuple(unwrappedSelector);
}
この定数がtrueに設定されている場合、最初見網状はキーセット、直接リターン開封されたセレクタを最適化されていないこと、一定DISABLE_KEY_SET_OPTIMIZATIONを定義します。最適化されたセレクタにネッティーだから?
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {
SelectionKey[] keys;
int size;
SelectedSelectionKeySet() {
keys = new SelectionKey[1024];
}
}
ない削除をサポートし、外部に含まれていることに加えて、下には、我々はあなたがポイントに見ることができ、selectedSelectionKeySetという名前のクラスを参照してください、それはAbstractSetを継承しますが、そのメンバ変数は、私たちはArrayListのを考え作られ、それが定義する方法を見てただのArrayListの簡易版のように見える、とさえ拡張をサポートしています。
はい、セットからとSelectionKeyのArrayListを交換するために、実際にネッティー反射の仕方によって、。私は慎重に思いますが、いくつかの真実の練習に圧倒さを感じます。時間とのArrayList HashSetの複雑さは、(1)Oランダムに見出されるが、アレイに直接オフセット必要HashSetのハッシュ演算による測位、時間がかかり、少しいくつかと比較されるが、それはよく知られています。シーンの使用と相まって、その後、コレクションをトラバースとSelectionKeyを取得して、設定の重複排除は完全に無関係な特徴、それはパフォーマンスの追求は、それを交換したいネッティー不思議ではありません。
ステップ3
NioEventLoopのworkerGroupを作成した後、どのようにnioEventLoopの仕事を選択するには、次に何をするネッティー希望の問題です。一般投票では、あなたがスレッドセレクタのバランスをとる同様の効果の負荷を作成する必要のあるプログラム、と考えるのは非常に簡単です。もちろん、熱狂的な網状のパフォーマンスの追求が容易に満足されていません。私たちは、このような一般的なシナリオとどのような操作で行われていたに網状に見えます。
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
// PowerOfTwo
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
// Generic
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
、xは、スレッドの数である場合に2の電力は、モジュロxの効果を達成するために、(X-1)であるとすることができる場合網状は、スレッドの数に応じてworkerGroupセレクタ内の2つの異なるスレッドを取り、見ることができます他の例では、ダイレクト・アクセス・モードを必要とします。これは強制的に同じ目的で、2のべき乗のハッシュマップ容量を設定されています。
ステップ4
ステップ4イベントループが閉じているときに、これらのリスナーが通知されます、いくつかの保証堅牢性を追加し、リスナーを追加することです。
ステップ5
読み取り専用のスレッドグループNioEventLoopを作成します。
このNioEventLoopGroupとNioEventLoopグループが作成含ま