序文
前の章- 「ネッティーソース分析シリーズサーバーの起動プロセスを解決するために、」私たちは、サーバが起動完了し、サーバーの起動が完了すると、クライアントアクセスおよび読み込みI / Oのイベントをどのようにどこから始めればありますか?そして、網状のボススレッドは、クライアント受信するTCPリンクする登録方法で接続要求ワーカースレッドプールを?これらの質問では、我々はアクセスに接続されたクライアントの読み取りおよび書き込みを開始するI / Oの解像度を。
1.NioEventLoop実行()開始
processSelectedKeys();
复制代码
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized(selectedKeys.flip());
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
复制代码
selectedKeysが空である、最適化採用するか否かを判断するselectedKeysに、進みprocessSelectedKeysOptimized。
private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
for (int i = 0;; i ++) {
final SelectionKey k = selectedKeys[i];
if (k == null) {
break;
}
selectedKeys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
...
}
}
复制代码
k.attachment()追加のオブジェクトを取得するためには、我々はそれまでどこの追加ですか?前の「ネッティーソースの解決-サーバーの起動プロセスを解決する」登録時に添付し、最大のオブジェクトを、実際には、NioServerSocketChannel自身を。
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
...
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
...
}
}
复制代码
私たちは、に戻るk.attachment()タイプかどうかを判断するために、追加のオブジェクトを削除した後、AbstractNioChannelは、私たちはここから見ることができ、されていない追加AbstractNioChannelのタイプは、追加そのNioTaskの我々が上に見える物体、AbstractNioChannelの、processSelectedKey()メソッド。
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final NioUnsafe unsafe = ch.unsafe();
...
int readyOps = k.readyOps();
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
if (!ch.isOpen()) {
return;
}
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
...
}
复制代码
動作は読み出し動作であるか、または接続作業が進む)(unsafe.read、2つのクラスがあり、この方法を実施する一AbstractNioByteChannel内部クラスNioByteUnsafe一AbstractNioMessageChannel内部クラスNioMessageUnsafe、これら2つのクラスがさNioUnsafeが実現クラスAbstractNioChannelのサブクラスが終了していることであるサブクラス、?私たちは見てNioServerSocketChannelを作成しているの作成時にNioByteUnsafeまたはNioMessageUnsafe。
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
}
复制代码
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
复制代码
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
}
复制代码
public abstract class AbstractNioChannel extends AbstractChannel {
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
}
}
复制代码
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected AbstractChannel(Channel parent) {
this.parent = parent;
unsafe = newUnsafe();
pipeline = new DefaultChannelPipeline(this);
}
}
复制代码
NioServerSocketChannelがあるAbstractNioMessageChannelの、サブクラスAbstractNioMessageChannelがあるAbstractNioChannelのサブクラス、newUnsafe()があるAbstractChannel抽象メソッド、そして我々はここから知っている、AbstractNioMessageChannelは実現()のnewUnsafe AbstractChannel裁判官は、我々が選択したことにより、抽象メソッド、AbstractNioMessageChannel内部をクラスNioMessageUnsafeの読み取り() 。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
...
for (;;) {
int localRead = doReadMessages(readBuf);
...
}
setReadPending(false);
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
pipeline.fireChannelReadComplete();
...
}
复制代码
ここでは、2つの部分で、1は、1つのイベントを処理している、メッセージを処理しています。
1.メッセージの処理
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
...
buf.add(new NioSocketChannel(this, ch));
return 1;
...
}
复制代码
受け入れクライアントたSocketChannel、カプセル化されたNioSocketChannelは、に追加リストのセット、私たちは見て()新しい新しいNioSocketChannel。
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
}
复制代码
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
@Override
protected AbstractNioUnsafe newUnsafe() {
return new NioByteUnsafe();
}
protected class NioByteUnsafe extends AbstractNioUnsafe {
@Override
public final void read() {
...
}
}
}
复制代码
AbstractNioByteChannelはまた、継承されたAbstractNioChannelを、そして実現newUnsafe()私たちは、クライアントが最初に接続したときと推論することができる方法を、服用AbstractNioMessageChannelのサブクラス読むのNioMessageUnsafe()クライアントが行くようにデータを送信し、それはあるAbstractNioByteChannel内部クラスAbstractNioUnsafe 読み取り()メソッド。
2.取扱いイベント
for (int i = 0; i < size; i ++) {
pipeline.fireChannelRead(readBuf.get(i));
}
复制代码
@Override
public ChannelPipeline fireChannelRead(Object msg) {
head.fireChannelRead(msg);
return this;
}
复制代码
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
final AbstractChannelHandlerContext next = findContextInbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(msg);
} else {
executor.execute(new OneTimeTask() {
@Override
public void run() {
next.invokeChannelRead(msg);
}
});
}
return this;
}
复制代码
次 の デバッグを 見ることができ、現在の ハンドラが ある ServerBootstrapAcceptorが 処理するために、このプロセッサ ChannelReadは、() メソッド、見ている場合 - 「サーバーの起動プロセス分析ネッティーソース解析」で 、これは知っている のinit() メソッド中 pipeline.addLast(新新ServerBootstrapAcceptor ()) 。なぜ p.addLast(新しいChannelInitializer())? ので ChannelInitializer.channelRegistered()は 現在削除させていただきます initChannelの プロセッサを。
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
initChannel((C) ctx.channel());
ctx.pipeline().remove(this);
ctx.fireChannelRegistered();
}
复制代码
私たちは、探し続けServerBootstrapAcceptorのChannelRead()メソッドを。
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
for (Entry<ChannelOption<?>, Object> e: childOptions) {
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) {
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);
}
}
复制代码
ここでは三つのステップ
(1)にchildHandlerは、プロセッサに加え、このから来ますか?これは設定当初から、あるserverBootstrap.childHandler(新しい新しいIOChannelInitializeを()) 。
(2)いくつかのパラメータを設定します。
(3)作業スレッドプールのレジスタクライアントチャンネル。
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
复制代码
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
复制代码
@Override
public EventExecutor next() {
return chooser.next();
}
复制代码
private final class GenericEventExecutorChooser implements EventExecutorChooser {
@Override
public EventExecutor next() {
return children[Math.abs(childIndex.getAndIncrement() % children.length)];
}
}
复制代码
作業を実行するスレッドプールのスレッドを選択し登録を。
@Override
public ChannelFuture register(Channel channel) {
return register(channel, new DefaultChannelPromise(channel, this));
}
复制代码
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
...
channel.unsafe().register(this, promise);
return promise;
}
复制代码
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new OneTimeTask() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
}
复制代码
@Override
protected void doRegister() throws Exception {
...
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
...
}
复制代码
プロセスの背後にあると「ネッティーソースの解析-サーバーの起動プロセス分析」の登録プロセスの、差がサービスをして起動時に登録されているのと同じであるボス登録タスクキュースレッドプールを実行し、クライアントが新しいアクセスに登録されています作業スレッドプールタスク実行キューレジスタ0()メソッド、および作業スレッドプールのセレクタに登録されたJava NIOここでいくつかの問題は、我々はの開口部に答えることができますどのようにクライアントアクセス?網状のボススレッドは、クライアントの受信TCP接続要求がへの登録をリンクする方法をワーカースレッドプールを?今、私たちは問題を残している:読み取りおよび書き込みI / Oのイベントをどのようにどこから始めればありますか?
私たちは、記事の最初に戻ります
private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
for (int i = 0;; i ++) {
final SelectionKey k = selectedKeys[i];
if (k == null) {
break;
}
selectedKeys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
...
}
}
复制代码
フロントボススレッドプールは、ここで登録へのクライアントアクセス接続とリンク完了し、ワーカースレッドプールのタスクキューを、追加読み取りイベントは今モニター作業スレッドの停止サイクルselectedKeysを保留中のイベントは、存在しませんイベント保留、次に実行processSelectedKey()メソッドを。
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
...
int readyOps = k.readyOps();
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
...
}
...
}
复制代码
ここでunsafe.read()を選択しAbstractNioByteChannelの読み取り() 。
@Override
public final void read() {
final ChannelConfig config = config();
if (!config.isAutoRead() && !isReadPending()) {
// ChannelConfig.setAutoRead(false) was called in the meantime
removeReadOp();
return;
}
final ChannelPipeline pipeline = pipeline();
final ByteBufAllocator allocator = config.getAllocator();
final int maxMessagesPerRead = config.getMaxMessagesPerRead();
RecvByteBufAllocator.Handle allocHandle = this.allocHandle;
if (allocHandle == null) {
this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();
}
ByteBuf byteBuf = null;
int messages = 0;
boolean close = false;
try {
int totalReadAmount = 0;
boolean readPendingReset = false;
do {
byteBuf = allocHandle.allocate(allocator);
int writable = byteBuf.writableBytes();
int localReadAmount = doReadBytes(byteBuf);
if (localReadAmount <= 0) {
// not was read release the buffer
byteBuf.release();
byteBuf = null;
close = localReadAmount < 0;
break;
}
if (!readPendingReset) {
readPendingReset = true;
setReadPending(false);
}
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
if (totalReadAmount >= Integer.MAX_VALUE - localReadAmount) {
totalReadAmount = Integer.MAX_VALUE;
break;
}
totalReadAmount += localReadAmount;
if (!config.isAutoRead()) {
break;
}
if (localReadAmount < writable) {
break;
}
} while (++ messages < maxMessagesPerRead);
pipeline.fireChannelReadComplete();
allocHandle.record(totalReadAmount);
if (close) {
closeOnRead(pipeline);
close = false;
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close);
} finally {
if (!config.isAutoRead() && !isReadPending()) {
removeReadOp();
}
}
}
}
复制代码
いくつかの部分にコードのこの大きなセグメント
1次まで設定サイクル時間、16時間は、終了しません選択は、読み続けmaxMessagesPerReadの 16にデフォルトを。
2。買収キャッシュ操作ハンドラ、config.getRecvByteBufAllocator()。NewHandle() 。
3。アプリケーションキャッシュスペース、allocHandle.allocate(アロケータ)。
4からソケットへのリードデータにbyteBufいます。
5次に読み出しイベントを渡すハンドラプロセッサ。
6。読書の時間を読んだ後、次の送信するハンドラ我々が詳細に解析され、その後、他の記事の内容との背中を見プロセッサ読まイベントを。
@Override
public ChannelPipeline fireChannelRead(Object msg) {
head.fireChannelRead(msg);
return this;
}
复制代码
@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
if (msg == null) {
throw new NullPointerException("msg");
}
final AbstractChannelHandlerContext next = findContextInbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(msg);
} else {
executor.execute(new OneTimeTask() {
@Override
public void run() {
next.invokeChannelRead(msg);
}
});
}
return this;
}
复制代码
ハンドラの
イベントのシーケンスがある
HeadContextHandler - > IdleStateHandler - > IOHandler - > TailContext
private void invokeChannelRead(Object msg) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
}
复制代码
IdleStateHandler
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
reading = true;
firstReaderIdleEvent = firstAllIdleEvent = true;
}
ctx.fireChannelRead(msg);
}
复制代码
読み込みイベントは、後に準備状態で検出された、真で設定し、これは、読み取りイベントを伝承していきIOHandler読んイベント。
public class IOHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
System.out.println(msg.toString());
}
...
}
复制代码
ユーザ定義にハンドラ処理読み取りイベント、リードためのI / Oイベントがどこ開始する方法であり、ユーザにどのハンドラプロセス完全に解析されました。
概要:
1.BOSSはスレッドNioServerSocketChannelをを受け入れるためのイベントやクライアント作業タスクキュー、タスク実行キューredister0()メソッド、読み取りするためのレジスタのイベント作業スレッドセレクタを。
2.workのスレッドポーリングselectkeys、イベントまでは、ユーザーにキャッシュされたデータを送信するためのハンドラ。