上一篇我们分析了ChannelInitializer加入到channelpipeline中,那么具体是如何加入到channelpipeline中的呢?
要解释这个必须先说明几个的东西
Channel的生命周期:
Channel 接口定义了四个方法,分别代表了Channel生命周期中回调的方法:
ChannelUnregistered Channel 已经被创建,但还未注册到 EventLoop
ChannelRegistered Channel 已经被注册到了 EventLoop
ChannelActive Channel 处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了
ChannelInactive Channel没有连接到远程节点
ChannelHandler
上一篇摘录的ChannelPipeline已经解释说明了ChannelHandler和ChannelPipeline的关系
ChannelHandler接口也有三个方法定义了处于该状态下的回调方法
handlerAdded 当把 ChannelHandler 添加到 ChannelPipeline 中时被调用
handlerRemoved 当从 ChannelPipeline 中移除 ChannelHandler 时被调用
exceptionCaught 当处理过程中在 ChannelPipeline 中有错误产生时被调用
Netty 定义了下面两个重要的 ChannelHandler 子接口:
ChannelInboundHandler——处理入站数据以及各种状态变化;
ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。
先看下ChannelInboundHandler接口中定义的方法:
public interface ChannelInboundHandler extends ChannelHandler {
//当channel已经注册到它的EventLoop并且能够处理I/O时被调用
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
//当Channel从它的EventLoop注销并且无法处理任何I/O时被调用
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
//Channel出于活动状态。就是连接建立
void channelActive(ChannelHandlerContext ctx) throws Exception;
//连接中断
void channelInactive(ChannelHandlerContext ctx) throws Exception;
//当从channel读取数据时被调用
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
//当channel上的一个读操作完成时
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
/**
* Gets called if an user event was triggered.
*/
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
/**
* Gets called once the writable state of a {@link Channel} changed. You can check the state with
* {@link Channel#isWritable()}.
*/
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
/**
* Gets called if a {@link Throwable} was thrown.
*/
@Override
@SuppressWarnings("deprecated")
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
如果你想自己写一个ChannelInboundHandler的实现类,但是只想重写ChannelInboundHandler接口中的部分方法,可以直接继承ChannelInboundHandlerAdapter。最典型的就是SimpleChannelInboundHandler
接下去说一下ChannelOutboundHandler
public interface ChannelOutboundHandler extends ChannelHandler {
//当请求将Channel绑定到本地时调用
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
//当请求将Channel连接到远程节点时被调用
void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception;
//当请求将Channel从远程节点断开时候调用
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
//当关闭请求时调用
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
//channel从EventLoop注销时调用
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
//当请求从Channel读取更多数据时被调用
void read(ChannelHandlerContext ctx) throws Exception;
//当请求通过channel写入到远程节点调用
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
//当请求通过 Channel 将入队数据冲刷到远程
节点时被调用
void flush(ChannelHandlerContext ctx) throws Exception;
接下去说明一下ChannelHandlerContext
ChannelHandlerContext 代表了 ChannelHandler 和 ChannelPipeline 之间的关 联,每当有 ChannelHandler 添加到 ChannelPipeline 中时,都会创建 ChannelHandler- Context。ChannelHandlerContext 的主要功能是管理它所关联的 ChannelHandler 和在 同一个 ChannelPipeline 中的其他 ChannelHandler 之间的交互。
下面一张表介绍了ChannelHandlerContext接口中的方法和作用
下面一张图展示了Channel、ChannelHandler以及ChannelHandlerContext之间的关系
我们回到DefaultChannelPipeline#addLast方法中
找到几个重要方法
newCtx = newContext(group, filterName(name, handler), handler);
---->
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
直接进入DefaultChannelHandlerContext的构造函数
private final ChannelHandler handler; DefaultChannelHandlerContext( DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) { super(pipeline, executor, name, isInbound(handler), isOutbound(handler)); if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; }
这里可以看到DefaultChannelHandlerContext持有handler的一个引用,也就是就是说一个handler对应一个context
进入父类AbstractChannelHandlerContext的构造函数
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, boolean inbound, boolean outbound) { this.name = ObjectUtil.checkNotNull(name, "name"); this.pipeline = pipeline; this.executor = executor; this.inbound = inbound;//判断是否是入站处理器 this.outbound = outbound;//判断是否是出站处理器 // Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor. ordered = executor == null || executor instanceof OrderedEventExecutor; }
addLast0(newCtx);
----->
private void addLast0(AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; prev.next = newCtx; tail.prev = newCtx; }
这段代码很简单,就是一个链表的重构,实际上就是把这个context加入到尾节点之前
接着走代码
如果registered是false,意味着channel没有在事件循环组中注册过,
if (!registered) {
newCtx.setAddPending();//将当前context挂起。
callHandlerCallbackLater(newCtx, true);//建立一个线程任务稍后执行。
return this;
}
callHandlerCallbackLater--->
assert !registered; PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx); PendingHandlerCallback pending = pendingHandlerCallbackHead; if (pending == null) { pendingHandlerCallbackHead = task; } else { //将新建的任务添加到链表里边 while (pending.next != null) { pending = pending.next; } pending.next = task; }
回到addLast0方法
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
//我们自己重写的handler的handlerAdded方法会被执行。
callHandlerAdded0(newCtx);
return this;
到这里为止,对addLast的流程已经比较清楚了,
找到ChannelInitailizer#initChannel方法
private boolean initChannel(ChannelHandlerContext ctx) throws Exception { if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance. try { initChannel((C) ctx.channel()); } catch (Throwable cause) { // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...). // We do so to prevent multiple calls to initChannel(...). exceptionCaught(ctx, cause); } finally { remove(ctx); } return true; } return false;
这里面注意会有一个 remove(ctx); 也就是说在调用完自定义的ChannelInitailizer的initChannel方法之后会把ChannelInitailizer从pipeline中删除。。
-----
在这里说一个小知识。
我们知道Channel和ChannelHandlerContext都继承了AttributeMap接口,在netty4.0时期,channel和channelHandlerContext的attr方法是获取的值时不一样的,netty官方时这么解释的
Channel 和ChannelHandlerContext都 实现了AttributeMap 用来设置用户自定义的属性。有时候Channel 和ChannelHandlerContext 都有自己的一套用户定义的属性(相互之间没有任何关系,即Channel 有自己的map,ChannelHandlerContext 也有自己的map)让用户感到非常困惑,比如我们使用 Channel.attr(KEY_X).set(valueX)设置一个key和value,但是没法通过ChannelHandlerContext.attr(KEY_X).get()方式获得,而且这样还浪费内存。
于是到了4.1就有了改动
为了解决这个问题,我们决定在每个Channel 内部只维护一个map,AttributeMap 永远使用AttributeKey 作为他的key,AttributeKey 保证在所有key之中是唯一的,这样就没有必要每个Channel定义多个属性,这样每个用户在ChannelHandler里边定义私有的静态属性的key(AttributeKey )就没有重复键的问
我们找到AbstractChannelHandlerContext#attr方法
@Override public <T> Attribute<T> attr(AttributeKey<T> key) { return channel().attr(key); }
可以看到直接调用channel的attr方法