一:Channel 与 ChannelPipeline
每个Channel在初始化时都会在其父类构造器中顺带构建一个ChannelPipeline,unSafe对象;即每个channel都有一个与之关联的channelPipeline;而channelPipeline是channelHandler承载的事件流容器,底层是以DefaultChannelHandlerContext为节点的双向链表;
而channelPipeline在初始化的时候就本身自带两个节点:head,tail;两个节点中的handler分别实现了ChannelOutBoundHandler,ChannelInBoundHandler;channel初始化时调用父类构造器如下:
protected AbstractChannel(Channel parent) {
this.parent = parent;
unsafe = newUnsafe();
pipeline = new DefaultChannelPipeline(this);
}
AbstractChannel 有一个 pipeline 字段, 在构造器中会初始化它为 DefaultChannelPipeline的实例. 这里的代码就印证了一点: 每个 Channel 都有一个 ChannelPipeline
.
接着我们跟踪一下 DefaultChannelPipeline 的初始化过程.
首先进入到 DefaultChannelPipeline 构造器中:
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;
}
DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutorGroup group, String name,
ChannelHandler handler) {
if (name == null) {
throw new NullPointerException("name");
}
if (handler == null) {
throw new NullPointerException("handler");
}
channel = pipeline.channel;
this.pipeline = pipeline;
this.name = name;
this.handler = handler;
if (group != null) {
// Pin one of the child executors once and remember it so that the same child executor
// is used to fire events for the same channel.
EventExecutor childExecutor = pipeline.childExecutors.get(group);
if (childExecutor == null) {
childExecutor = group.next();
pipeline.childExecutors.put(group, childExecutor);
}
executor = childExecutor;
} else {
executor = null;
}
inbound = handler instanceof ChannelInboundHandler;
outbound = handler instanceof ChannelOutboundHandler;
}
这里的TailHandler 与 headHandler 分别实现ChannelInboundHandler,ChannelOutboundHandler接口;然后通过DefaultChannelHandlerContext进行封装成两个对应的链表节点,相互指向形成双链表;这里4.1.33版本里的每个head,tail即使channelHandler,又是handlerContext,只是用一个类涵盖完了。同时DefaultChannelHandlerContext节点中的两个boolean值inbound ,outBound 是对类型的判断结果,这个两个布尔值是pipeline中handler事件流转的关键,而pipeline中事件总是从outBound流向inbound;
二:自定义ChannelInitializer 的添加
上节初始化的pipeline只是两个内部实例的channelHandler,并没有用户自定的handler,回到代码最初,看一下,自定义的handler如何添加到pipeline中;
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoClientHandler());
}
});
上面代码的初始化过程, 相信大家都不陌生. 在调用 handler 时, 传入了 ChannelInitializer 对象, 它提供了一个 initChannel 方法供我们初始化 ChannelHandler. 那么这个初始化过程是怎样的呢? 下面我们就来揭开它的神秘面纱.首先看一下ChannleInitializer类结构;
ChannelInitializer 实现了 ChannelHandler, 而且还是inBound类型的,那么它是在什么时候添加到 ChannelPipeline 中的呢? 进行了一番搜索后, 我们发现它是在 Bootstrap.init 方法中添加到 ChannelPipeline 中的.
其代码如下:
final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel();
try {
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
ChannelFuture regFuture = group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(handler());
final Map<ChannelOption<?>, Object> options = options();
synchronized (options) {
for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
try {
if (!channel.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: " + channel, t);
}
}
}
final Map<AttributeKey<?>, Object> attrs = attrs();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
}
上面的代码将 handler() 返回的 ChannelHandler 添加到 Pipeline 中, 而 handler() 返回的是handler 其实就是我们在初始化 Bootstrap 调用 handler 设置的 ChannelInitializer 实例, 因此这里就是将 ChannelInitializer 插入到了 Pipeline 的末端.
此时 Pipeline 的结构如下图所示:
由于ChannelInitializer是一个channelHandler,而链表的节点是DefaultChannelHandlerContext;因此该ChannelInitializer肯定会再次被封装;看代码:
public ChannelPipeline addLast(EventExecutorGroup group, final String name, ChannelHandler handler) {
synchronized (this) {
checkDuplicateName(name);
DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
addLast0(name, newCtx);
}
return this;
}
private void addLast0(final String name, DefaultChannelHandlerContext newCtx) {
checkMultiplicity(newCtx);
DefaultChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
name2ctx.put(name, newCtx);
callHandlerAdded(newCtx);
}
由于ChannelInitializer是实现ChannelInboundHandler接口的,因此封装的DefaultChannelHandlerContext中inbound布尔值为true;
二:2 自定义 ChannelHandler 的添加过程
上节只是将CannelInitializer添加到pipeline中,而其初始化方法中的自定义handler合适被添加,这里只需查找CannelInitializer中initChannel合适被调用即可。首先对
在上一小节中, 我们已经分析了一个 ChannelInitializer 如何插入到 Pipeline 中的, 接下来就来探讨一下 ChannelInitializer 在哪里被调用, ChannelInitializer 的作用, 以及我们自定义的 ChannelHandler 是如何插入到 Pipeline 中的.客户端中 channel 的注册过简单地复习一下:
-
首先在 AbstractBootstrap.initAndRegister中, 通过 group().register(channel), 调用 MultithreadEventLoopGroup.register 方法
-
在MultithreadEventLoopGroup.register 中, 通过 next() 获取一个可用的 SingleThreadEventLoop, 然后调用它的 register
-
在 SingleThreadEventLoop.register 中, 通过 channel.unsafe().register(this, promise) 来获取 channel 的 unsafe() 底层操作对象, 然后调用它的 register.
-
在 AbstractUnsafe.register 方法中, 调用 register0 方法注册 Channel
-
在 AbstractUnsafe.register0 中, 调用 AbstractNioChannel#doRegister 方法
-
AbstractNioChannel.doRegister 方法通过 javaChannel().register(eventLoop().selector, 0, this) 将 Channel 对应的 Java NIO SockerChannel 注册到一个 eventLoop 的 Selector 中, 并且将当前 Channel 作为 attachment.
而我们自定义 ChannelHandler 的添加过程, 发生在 AbstractUnsafe.register0 中, 在这个方法中调用了 pipeline.fireChannelRegistered() 方法, 其实现如下:
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!ensureOpen(promise)) {
return;
}
doRegister();
registered = true;
promise.setSuccess();
pipeline.fireChannelRegistered();
if (isActive()) {
pipeline.fireChannelActive();
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
if (!promise.tryFailure(t)) {
logger.warn(
"Tried to fail the registration promise, but it is complete already. " +
"Swallowing the cause of the registration failure:", t);
}
}
}
@Override
public ChannelPipeline fireChannelRegistered() {
head.fireChannelRegistered();
return this;
}
@Override
public ChannelPipeline fireChannelRegistered() {
head.fireChannelRegistered();
return this;
}
而head.fireChannlelRegistered方法调用其DefaultChannelHandlerContext节点里方法:
@Override
public ChannelHandlerContext fireChannelRegistered() {
final DefaultChannelHandlerContext next = findContextInbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
return this;
}
private DefaultChannelHandlerContext findContextInbound() {
DefaultChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
而在findContextInbound是在双链表中查找第一个布尔值inbound为true的节点;而上节我们添加channelInitializer就是第一节点;当获取到 inbound 的 Context 后, 就调用它的 invokeChannelRegistered 方法:
private void invokeChannelRegistered() {
try {
((ChannelInboundHandler) handler).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
}
invokeChannelRegistered方法中的handler就是封装了DefaultChannelHandlerContext节点中的ChannelInitializer;然后触发其channelRegistered方法;
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ChannelPipeline pipeline = ctx.pipeline();
boolean success = false;
try {
initChannel((C) ctx.channel());
pipeline.remove(this);
ctx.fireChannelRegistered();
success = true;
} catch (Throwable t) {
logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
} finally {
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
if (!success) {
ctx.close();
}
}
}
而上述initChannel方法的实现就是我们客户端代码辅助类添加的handler时所实现的initChannel方法;这里传入的channel即辅助类启动时指明的NioStockChannel 或者serverNioStockChannel
.handler(new ChannelInitializer<Channel>() {
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(channelHandler);
};
});
这里自定义的handler添加完毕后,会将ChannelInitializer 这个ChannelHandler从pipeline中移除掉;
因此当调用了这个方法后, 我们自定义的 ChannelHandler 就插入到 Pipeline 了, 此时的 Pipeline 如下图所示:
当添加了自定义的 ChannelHandler 后, 会删除 ChannelInitializer 这个 ChannelHandler, 即 "ctx.pipeline().remove(this)", 因此最后的 Pipeline 如下:
总结:每个channel基本都会经历以下阶段
(1):首先经过辅助类进行各种初始化;
a: ChannelFactory 工厂实现类的初始化; channel 是通过ChannelFactory 基于工厂模式,底层经过反射初始化而成;由于只有在启动时才会对Channel进行构建,也就反射一次,可以忽略性能影响;
b:Reacto模式线程池初始化;c:heanler类的初始化,这里的handler类型是netty内部的handler类,非自定义的;
(2)channel的注册,初始化,添加自定handler等这些操作,都是在辅助类在启动bind方法或者connect方法时才开始执行的
a:初始化channel,通过channel工厂newInstance方法,构建指定的Chanler
b:初始化channelPipeline,构建双链表结构的pipeline容器;
c:初始化unsafe类,这个接口是实现与底层stock通信的功能类;
d:添加自定义ChannelHander
f: 将channel与EventLoop进行绑定,即注册到selectot模型中;然后触发各种IO事件;