2. Netty 源码解析之ChannelPipeline
通过对 BootStrap 分析得到,在channel初始化时,会创造一个channelPipeline,代码如下:
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = this.newId();//唯一标识
this.unsafe = this.newUnsafe();
this.pipeline = this.newChannelPipeline();
}
也知道了,每个channelPipeline实际是一个双重环形链表,代码如下:
protected DefaultChannelPipeline(Channel channel) {
this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
this.voidPromise = new VoidChannelPromise(channel, true);
this.tail = new DefaultChannelPipeline.TailContext(this);
this.head = new DefaultChannelPipeline.HeadContext(this);
this.head.next = this.tail;
this.tail.prev = this.head;
}
1. ChannelInitializer 作用
在 Bootstrap.init
方法中会调用 p.addLast()
方法,将 ChannelInitializer 插入到链表的末端,代码如下:
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(new ChannelHandler[]{this.config.handler()});
Map<ChannelOption<?>, Object> options = this.options0();
synchronized(options) {
setChannelOptions(channel, options, logger);
}
Map<AttributeKey<?>, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator var6 = attrs.entrySet().iterator();
while(var6.hasNext()) {
Entry<AttributeKey<?>, Object> e = (Entry)var6.next();
channel.attr((AttributeKey)e.getKey()).set(e.getValue());
}
}
}
下面分析下 addLast
方法,代码如下:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized(this) {
checkMultiplicity(handler);
newCtx = this.newContext(group, this.filterName(name, handler), handler);
this.addLast0(newCtx);
if (!this.registered) {
newCtx.setAddPending();
this.callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
public void run() {
DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
}
});
return this;
}
}
this.callHandlerAdded0(newCtx);
return this;
}
代码中看到,newContext
中创建了一个Context,创建的代码如下:
DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
} else {
this.handler = handler;
}
}
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
代码中,发现 ChannelInitializer 仅仅实现了 ChannelInboundHandler 接口,因此这里实例化的 DefaultChannelHandlerContext 的 inbound = true,outbound = false。
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = this.tail.prev;
newCtx.prev = prev;
newCtx.next = this.tail;
prev.next = newCtx;
this.tail.prev = newCtx;
}
2. 自定义 ChannelHandler 的添加过程
在channel注册调用链中,AbstractUnsafe.register0
方法中调用了pipeline.fireChannelRegistered()
方法进行添加自定义handler
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
}
boolean firstRegistration = this.neverRegistered;
AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true;
AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
this.safeSetSuccess(promise);
AbstractChannel.this.pipeline.fireChannelRegistered();
if (AbstractChannel.this.isActive()) {
if (firstRegistration) {
AbstractChannel.this.pipeline.fireChannelActive();
} else if (AbstractChannel.this.config().isAutoRead()) {
this.beginRead();
}
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
}
}
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(this.head);
return this;
}
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
public void run() {
next.invokeChannelRegistered();
}
});
}
}
private void invokeChannelRegistered() {
if (this.invokeHandler()) {
try {
((ChannelInboundHandler)this.handler()).channelRegistered(this);
} catch (Throwable var2) {
this.notifyHandlerException(var2);
}
} else {
this.fireChannelRegistered();
}
}
private boolean invokeHandler() {
int handlerState = this.handlerState;
return handlerState == 2 || !this.ordered && handlerState == 1;
}
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(this.findContextInbound());
return this;
}
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while(!ctx.inbound);
return ctx;
}
private static final int ADD_PENDING = 1;
private static final int ADD_COMPLETE = 2;
private static final int REMOVE_COMPLETE = 3;
很显然,这个代码会从 head 开始遍历 Pipeline 的双向链表,然后找到第一个属性 inbound为 true 的 ChannelHandlerContext 实例。ChannelInitializer 实现了 ChannelInboudHandler,并接着调用了 ChannelInitializer.channelRegistered
方法。
接下来看一下, ChannelInitializer 类内到底有什么玄机:
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
if (this.initChannel(ctx)) {
ctx.pipeline().fireChannelRegistered();
} else {
ctx.fireChannelRegistered();
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (this.initMap.putIfAbsent(ctx, Boolean.TRUE) == null) {
try {
this.initChannel(ctx.channel());
} catch (Throwable var6) {
this.exceptionCaught(ctx, var6);
} finally {
this.remove(ctx);
}
return true;
} else {
return false;
}
}
private void remove(ChannelHandlerContext ctx) {
try {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
} finally {
this.initMap.remove(ctx);
}
}
发现channelRegistered
方法中调用了channel初始化的工作,即添加 ChannelHandler 到 ChannelPipeline,完成后会移除 ChannelInitializer。其中, ctx.pipeline().fireChannelRegistered()
添加注册事件。
3. handler命名
关于handler的命名问题,通过跟踪代码发现,当没有自定义handler名称时,系统会自动创建一个命名,代码如下:
private String generateName(ChannelHandler handler) {
Map<Class<?>, String> cache = (Map)nameCaches.get();
Class<?> handlerType = handler.getClass();
String name = (String)cache.get(handlerType);
if (name == null) {
name = generateName0(handlerType);
cache.put(handlerType, name);
}
if (this.context0(name) != null) {
String baseName = name.substring(0, name.length() - 1);
int i = 1;
while(true) {
String newName = baseName + i;
if (this.context0(newName) == null) {
name = newName;
break;
}
++i;
}
}
return name;
}
private static String generateName0(Class<?> handlerType) {
return StringUtil.simpleClassName(handlerType) + "#0";
}
4. Pipeline 事件传输机制
官网中对于 channelpipeline 的介绍如下:
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) |
+-------------------------------------------------------------------+
描述了ChannelPipeline中ChannelHandler通常如何处理I/O事件。 I/O事件由ChannelInboundHandler或ChannelOutboundHandler处理,并通过调用ChannelHandlerContext中定义的事件传播方法。
从上图可以看出,inbound 事件和 outbound 事件的流向是不一样的,inbound 事件的流行是从下至上,而 outbound 刚好相反,是从上到下。并且 inbound 的传递方式是通过调用相应的 ChannelHandlerContext.fireIN_EVT()
方法,而 outbound 方法的的传递方式是通过调用 ChannelHandlerContext.OUT_EVT()
方法``。 例 如 ChannelHandlerContext.fireChannelRegistered()
调用会发送一个 ChannelRegistered 的
inbound 给下一个 ChannelHandlerContext,而 ChannelHandlerContext.bind 调用会发送一个 bind 的 outbound 事件给下一个 ChannelHandlerContext。
Inbound 事件传播方法有:
ChannelHandlerContext.fireChannelRegistered()
ChannelHandlerContext.fireChannelActive()
ChannelHandlerContext.fireChannelRead(Object)
ChannelHandlerContext.fireChannelReadComplete()
ChannelHandlerContext.fireExceptionCaught(Throwable)
ChannelHandlerContext.fireUserEventTriggered(Object)
ChannelHandlerContext.fireChannelWritabilityChanged()
ChannelHandlerContext.fireChannelInactive()
ChannelHandlerContext.fireChannelUnregistered()
Outbound 事件传播方法有:
ChannelOutboundInvoker.bind(SocketAddress, ChannelPromise)
ChannelOutboundInvoker.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelOutboundInvoker.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelOutboundInvoker.disconnect(ChannelPromise)
ChannelOutboundInvoker.close(ChannelPromise)
ChannelOutboundInvoker.deregister(ChannelPromise)
注意,如果我们捕获了一个事件,并且想让这个事件继续传递下去,那么需要调用 Context 相应的传播方法。
5. Outbound 的操作
Outbound 事件都是请求事件,传播方向是 tail -> 自定义context -> head。在 bootstrap 分析中,客户端段连接和服务器绑定socket都是outbound操作,具体相关代码如下:
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return this.tail.bind(localAddress, promise);
}
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
} else if (this.isNotValidPromise(promise, false)) {
return promise;
} else {
final AbstractChannelHandlerContext next = this.findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {// 判断执行线程(Thread.currentThread())是否是当前线程
// 使用当前IO线程执行用户自定义处理
next.invokeBind(localAddress, promise);
} else {
// 使用用户定义的线程执行处理过程
safeExecute(executor, new Runnable() {
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, (Object)null);
}
return promise;
}
}
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (this.invokeHandler()) {//如果handler状态为可被调用
try {
((ChannelOutboundHandler)this.handler()).bind(this, localAddress, promise);
} catch (Throwable var4) {
notifyOutboundHandlerException(var4, promise);
}
} else {
this.bind(localAddress, promise);//等待,这里为啥设计成环形链表的原因,只要不是可调用状态,会一直循环这个环链表
}
}
代码中,executor.inEventLoop()
进行判断是否切换线程。Netty为用户提供了方便的线程切换处理,特别是在版本4.1后,可以直接使用JDK提供的线程池,在一个处理器中如果有耗时较大的业务逻辑,可以指定该处理器的处理线程Executor,以避免占用IO线程造成性能损失。
private boolean invokeHandler() {
// handlerState为volatile变量,存储为本地变量,以便减少volatile读
int handlerState = this.handlerState;
return handlerState == 2 || !this.ordered && handlerState == 1;
}
从代码中看出,当一个处理器还没有调用 HandlerAdded 方法时,或者处理器的执行线程是非顺序线程池的实例,才能执行业务处理逻辑;否则必须等待已调用 handlerAdded 方法,才能处理业务逻辑。这部分的处理,保证了ChannelPipeline的线程安全性,由此用户可以随意增加删除Handler。当可以处理业务逻辑时,调用 ChannelOutboundHandlerAdapter.bind
,轮询到 HeadContext 时,调用它的bind方法做业务处理。
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {
...
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
ctx.bind(localAddress, promise);
}
...
}
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
this.unsafe.bind(localAddress, promise);
}
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
this.javaChannel().bind(localAddress, this.config.getBacklog());
} else {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
}
6. Inbound 事件
Inbound 事件是一个通知事件,传播方向是 head -> 自定义Context -> tail。怎么找到 Outbound 和 Inbound 事件之间的联系呢?
还以bind为例,具体业务逻辑处理过程,代码如下:
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
this.assertEventLoop();
if (promise.setUncancellable() && this.ensureOpen(promise)) {
...
boolean wasActive = AbstractChannel.this.isActive();
try {
AbstractChannel.this.doBind(localAddress);
} catch (Throwable var5) {
this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}
if (!wasActive && AbstractChannel.this.isActive()) {
this.invokeLater(new Runnable() {
public void run() {
AbstractChannel.this.pipeline.fireChannelActive();
}
});
}
this.safeSetSuccess(promise);
}
}
当 doBind
绑定完socket之后,会调用 AbstractChannel.this.pipeline.fireChannelActive()
进行inbound事件,将socket绑定成功的消息传递出去,消息是从head开始的。接着会调用 ChannelInboundHandlerAdapter.fireChannelActive
,轮询handler,直到 TailContext 将消息传递出去。
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(this.head);
return this;
}
public ChannelHandlerContext fireChannelActive() {
invokeChannelActive(this.findContextInbound());
return this;
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
DefaultChannelPipeline.this.onUnhandledInboundChannelActive();
}
TailContext 的 Inbound 处理方法时,它们的实现都是空的,也就是说如果是 Inbound,当用户没有实现自定义的处理器时,那么默认是不处理的。