前言
netty采用了类似责任链模式,inbound和outbound分别入栈和出栈顺序执行每个channelHanderContext。整体分为三个类:
1>ChannelPipe 操作(添加删除)context;控制context流动
2>ChannelHandlerContext 持有前一个、后一个ChannelHandlerContext;持有ChannelHandler处理事件
3>ChannelHandler 处理当前业务逻辑
I/O Request
via Channel or
ChannelHandlerContext
|
+---------------------------------------------------+---------------+
| ChannelPipeline | |
| \|/ |
| +---------------------+ +-----------+----------+ |
| | Inbound Handler N | | Outbound Handler 1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler N-1 | | Outbound Handler 2 | |
| +----------+----------+ +-----------+----------+ |
| /|\ . |
| . . |
| ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
| [ method call] [method call] |
| . . |
| . \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 2 | | Outbound Handler M-1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 1 | | Outbound Handler M | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
+---------------+-----------------------------------+---------------+
| \|/
+---------------+-----------------------------------+---------------+
| | | |
| [ Socket.read() ] [ Socket.write() ] |
| |
| Netty Internal I/O Threads (Transport Implementation) |
+-------------------------------------------------------------------+
注:其中channel.write和channelHandlerContext.write区别在于channel.write会从tai开始,遍历所有的outboundHandler;channelHandlerContext.write会从当前channelContext开始,遍历余下的outboundHandler。
一.读取数据
NioEventLoop处理读事件,NioEventLoo的processSelectedKey方法,
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop == this) {
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
}
return;
}
try {
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
//处理读事件
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
unsafe.read()分两种:
1.OP_ACCEPT:bossgroup连接事件,后续注册读写到workergroup;处理的类为AbstractNioMessageChannel
2.OP_READ:workgroup的读事件,获取客户端的输入并传入channelPipeline处理业务逻辑;处理的类为AbstractNioByteChannel
处理读写事件,进入DefaultChannelPipeline(包含head和tail的ChannelHandlerContext)中,读取事件从head开始,写时间从tail开始。
下面是责任链读取执行的过程:
如果中间的ChannelHandler一直调用fireChannelRead传递下去则会到TailContext,然后直接释放掉buf
二.输出数据
和读取数据逻辑几乎一致,注意其中channel.write和channelHandlerContext.write区别在于channel.write会从tai开始,遍历所有的outboundHandler;channelHandlerContext.write会从当前channelContext开始,遍历余下的outboundHandler。
输出若不flush写入到ChannelOutboundBuffer类中,是一个增加的链表,默认大小为
INT DEFAULT_HIGH_WATER_MARK = 64 * 1024
可以通过设置WRITE_BUFFER_HIGH_WATER_MARK调整大小
bootstrap.childOption(WRITE_BUFFER_HIGH_WATER_MARK, 1024);
调用writeAndFlush会将之前的ChannelOutboundBuffer内容一起输出。