所谓Netty5的事件分发模型,主要指的是ChannelPipline, ChannelHandlerContext, ChannelHandlerInvoker, ChannelHandler, Unsafe 这几个核心接口之间的交互模型。
首先是ChannelPipline接口,它是Netty中事件分发的路径。每个Channel都会绑定一个ChannelPipeline来分发事件。
public interface Channel extends AttributeMap, Comparable<Channel> {
ChannelPipeline pipeline();
}
ChannelPipeline是一个大的接口,是一个Facade门面模式的实例。它主要包括三部分的接口,
1. 链表的接口,包括各种遍历,修改链表的操作
2. inbound事件的接口,以fireXXXX开头的方法
3. outbound事件的接口, 不带fire的方法,比如read, write,bind, connect等
inbound,outbound事件是Netty抽象的事件概念,从底层IO事件到用户事件的方向是inbound事件,从用户事件到底层IO事件的方向是outbound事件
DefaultChannelPipeline是ChannelPipeline接口的具体实现,它处理实际的事件分发。它采用了两个单链表head, tail 来处理inbound,outbound事件。
单链表的节点是ChannelHandlerContext,它通过next, prev两个指针指向前后的节点。
head链表的第一个节点是HeadHandler, tail节点的第一个节点是TailHandler。 HeadHandler里面封装了Unsafe接口, 来进行实际的IO读写。inbound事件从底层IO开始,outbound事件到底层IO结束,所以inbound事件链的起点从HeadHandler开始,outbound事件链的终点在HeadHandler结束
在上一篇将Netty如何注册OP_ACCEPT,OP_READ事件时 http://blog.csdn.net/iter_zc/article/details/39396169,我们看到每次Channel注册到Selector时,只设置了Attachment,没有真正的注册,真正的注册是在注册后调用Channel.read,通过HeadHandler的read方法,调用到AbstractChannel的doBeginRead来把OP_ACCEPT,OP_READ设置到SelectionKey的interestOps中
final class DefaultChannelPipeline implements ChannelPipeline {
final DefaultChannelHandlerContext head;
final DefaultChannelHandlerContext tail;
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
throw new NullPointerException("channel");
}
this.channel = channel;
TailHandler tailHandler = new TailHandler();
tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
HeadHandler headHandler = new HeadHandler(channel.unsafe());
head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
head.next = tail;
tail.prev = head;
}
}
final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext {
volatile DefaultChannelHandlerContext next;
volatile DefaultChannelHandlerContext prev;
private final AbstractChannel channel;
private final DefaultChannelPipeline pipeline;
private final String name;
private final ChannelHandler handler;
private boolean removed;
final int skipFlags;
// Will be set to null if no child executor should be used, otherwise it will be set to the
// child executor.
final ChannelHandlerInvoker invoker;
private ChannelFuture succeededFuture;
}
static final class HeadHandler extends ChannelHandlerAdapter {
protected final Unsafe unsafe;
protected HeadHandler(Unsafe unsafe) {
this.unsafe = unsafe;
}
}
ChannelHandlerContext,它相当于链表中的节点,从上面的代码中可以看到,它关联了一个ChannelHandler和ChannelHandlerInvoker。
ChannelHandlerInvoker封装了线程池调用ChannelHandler的接口。DefaultChannelHandlerInvoker是默认实现,它关联了一个EventExecutor来实际执行ChannelHandler,这里实际的对象是NioEventLoop,使用单线程来执行一个Channel所有的ChannelHandler。
DefaultChannelHandlerInvoker使用了ChannelHandlerInvokerUtil来调用ChannelHandler的方法。这里我们看到ChannelHandlerInvoker的模型
1. DefaultChannelHandlerInvoker负责选择执行的线程
2. ChannelHandlerInvokerUtil调用ChannelHandler的方法
public class DefaultChannelHandlerInvoker implements ChannelHandlerInvoker {
private final EventExecutor executor;
@Override
public void invokeChannelRegistered(final ChannelHandlerContext ctx) {
if (executor.inEventLoop()) {
invokeChannelRegisteredNow(ctx);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
invokeChannelRegisteredNow(ctx);
}
});
}
}
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
private final ChannelHandlerInvoker invoker = new DefaultChannelHandlerInvoker(this);
}
public final class ChannelHandlerInvokerUtil {
public static void invokeChannelRegisteredNow(ChannelHandlerContext ctx) {
try {
ctx.handler().channelRegistered(ctx);
} catch (Throwable t) {
notifyHandlerException(ctx, t);
}
}
}
DefaultChannelHandlerContext还保存了一个它关联的ChannelHandler的skipflag信息。skipflag指的是一个ChannelHandler可以使用@Skip注解来表示它不处理某个方法。DefaultChannelHandlerContext记录了这些信息,可以在事件到来的时候直接判断是否要跳过处理。
private static int skipFlags0(Class<? extends ChannelHandler> handlerType) {
int flags = 0;
try {
if (handlerType.getMethod(
"handlerAdded", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
flags |= MASK_HANDLER_ADDED;
}
if (handlerType.getMethod(
"handlerRemoved", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
flags |= MASK_HANDLER_REMOVED;
}
if (handlerType.getMethod(
"exceptionCaught", ChannelHandlerContext.class, Throwable.class).isAnnotationPresent(Skip.class)) {
flags |= MASK_EXCEPTION_CAUGHT;
}
if (handlerType.getMethod(
"channelRegistered", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
flags |= MASK_CHANNEL_REGISTERED;
}
}
ChannelHandler接口定义了如何处理inbound和outbond事件的方法,是职责链节点的具体实现,也是业务代码编写地方。Netty抽象了Pipeline这个职责链,将底层IO处理和业务处理进行了隔离,使用户可以关注与业务逻辑的实现。
public interface ChannelHandler {
///
// Inbound event handler methods //
///
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
// Outbound event handler methods //
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception;
}
Unsafe定义了实际IO处理的接口,它的含义不是说它的方法是不安全的,而是说它的接口是给框架本身调用的,不要暴露给业务层调用。
Unsafe的最底层实现类采用了模板方法模式,NioMessageUnsafe绑定到了NioServerSocketChannel,NioByteUnsafe绑定到NioSocketChannel,
最终的IO读写方法实现在NioServerSocketChannel和NioByteUnsafe中,调用了Java的ServerSocketChannel和SocketChannel来实现。
interface Unsafe {
ChannelHandlerInvoker invoker();
SocketAddress localAddress();
SocketAddress remoteAddress();
void register(ChannelPromise promise);
void bind(SocketAddress localAddress, ChannelPromise promise);
void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
void disconnect(ChannelPromise promise);
void close(ChannelPromise promise);
void closeForcibly();
void beginRead();
void write(Object msg, ChannelPromise promise);
void flush();
ChannelPromise voidPromise();
ChannelOutboundBuffer outboundBuffer();
}
<pre name="code" class="java">
private final class NioByteUnsafe extends AbstractNioUnsafe {
private RecvByteBufAllocator.Handle allocHandle;
@Override
public void read() {
final ChannelConfig config = config();
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();
}
if (!config.isAutoRead()) {
removeReadOp();
}
ByteBuf byteBuf = null;
int messages = 0;
boolean close = false;
try {
int byteBufCapacity = allocHandle.guess();
int totalReadAmount = 0;
do {
byteBuf = allocator.ioBuffer(byteBufCapacity);
int writable = byteBuf.writableBytes();
int localReadAmount = doReadBytes(byteBuf);
if (localReadAmount <= 0) {
// not was read release the buffer
byteBuf.release();
close = localReadAmount < 0;
break;
}
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
if (totalReadAmount >= Integer.MAX_VALUE - localReadAmount) {
// Avoid overflow.
totalReadAmount = Integer.MAX_VALUE;
break;
}
totalReadAmount += localReadAmount;
if (localReadAmount < writable) {
// Read less than what the buffer can hold,
// which might mean we drained the recv buffer completely.
break;
}
} while (++ messages < maxMessagesPerRead);
pipeline.fireChannelReadComplete();
allocHandle.record(totalReadAmount);
if (close) {
closeOnRead(pipeline);
close = false;
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close);
}
}
}
//NioSocketChannel.doReadBytes()调用SocketChannel来读取数据
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
}