netty探索之旅六

netty中的管道--ChannelPipeline的事件传输机制

在AbstractChannelHandlerContext对象中有inbound和outbound两个boolean变量,用于标识 Context所对应的handler的类型。
inbound=ture,表示对应的ChannelHandler实现了 ChannelInboundHandler方法.
outbound=ture,表示对应的ChannelHandler实现了 ChannelOutboundHandler方法.

在netty中支持两种类型的事件: Inbound和Outbound事件。
看看以下的图:

[img]http://dl2.iteye.com/upload/attachment/0123/4528/8fa097f7-07ad-380f-9b2e-f299389be76f.png[/img]
inbound事件的流向是从下至上的,outbound事件的流向是从上至下的,inbound事件的传递方式是通过调用相应的ChannelHandlerContext.fire_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()


Oubound事件传输方法有:

ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelHandlerContext.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelHandlerContext.disconnect(ChannelPromise)
ChannelHandlerContext.close(ChannelPromise)


这些方法都在ChannelHandlerContext接口中定义。

Oubound:
[color=blue]Outbound事件都是请求事件(request event), 即请求某件事情的发生,然后通过Outbound事件进行通知。Outbound事件的传播方向是tail-->customContext-->head[/color]

接下来我们以connect事件,分析一下Outbound事件的传播机制。
在EchoClient实例中调用connect方法:

ChannelFuture f = b.connect(HOST, PORT).sync();

调用链是:Bootstrap.connect -> Bootstrap.doConnect -> Bootstrap.doConnect0 -> AbstractChannel.connect
在AbstractChannel.connect中其实就是调用DefaultChannelPipeline.connect的方法:

@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, promise);
}

看看DefaultChannelPipeline的connect方法:

@Override
public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, promise);
}

当connect事件(outbound事件)到达Pipeline后,就是以tail为起点开始传播的。tail.connect调用到的是:AbstractChannelHandlerContext.connect方法:

public ChannelFuture connect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
return promise;
}

final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeConnect(remoteAddress, localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeConnect(remoteAddress, localAddress, promise);
}
}, promise, null);
}
return promise;
}

findContextOutbound这段代码:

private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}

它的作用是以当前Context为起点(tail),向Pipeline中的双向链表的前端寻找第一个outbound 属性为true的Context。当我们找到了一个outbound的Context时,就调用它的invokeConnect方法。

private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
connect(remoteAddress, localAddress, promise);
}
}

方法里面会调用与之关联的handler的connect方法,就是handler()获取的handler,如果我们自己实现的handle没有重写ChannelHandler的connect方法,那么会调用 ChannelOutboundHandlerAdapter所实现的connect方法:

@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception {
ctx.connect(remoteAddress, localAddress, promise);
}

这个方法又回到了AbstractChannelHandlerContext的connect方法了。又会找到从此context开始的第一个outbound为true的context。
循环链为:
Context.connect-->Connect.findContextOutbound-->next.invokeConnect--> handler.connect-->Context.connect。
这样的循环会把事件传递到pipeline双向链表中的头节点(head),head实现了ChannelOutboundHandler接口,outbound的属性为true。head本身是一个ChannelHandlerContext也是一个ChannelOutboundHandler,因此在connect事件到达head后,因为HeadContext中覆盖了handler()方法,因此在invokeConnect方法中的handler()返回的就是HeadContext,这样就会调用的HeadContext.connect的方法:

public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
unsafe.connect(remoteAddress, localAddress, promise);
}

这样的一个outbound事件就完成了。其他的outbound事件和connect事件都是遵循一个的传播规则。

Inbound:
Inbound事件是一个通知事件,即某件事情已经发生了,然后通过Inbound事件进行通知。Inbound事件通常发生在Channel的状态改变或IO事件就绪。
Inbound事件的的传播方向是:head-->customContext-->tail。

既然我们分析了Connect这个Outbound事件,那么接着分析Connect事件后会发生什么Inbound 事件,并最终找到Outbound和Inbound事件之间的联系。
上面的unsafe.connect调用的是AbstractNioUnsafe.connect:

...............
if (doConnect(remoteAddress, localAddress)) {
fulfillConnectPromise(promise, wasActive);
}
..............

doConnect就是进行实际的Socket连接。在NioSocketChannel中

protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {
doBind0(localAddress);
}

boolean success = false;
try {
boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true;
return connected;
} finally {
if (!success) {
doClose();
}
}
}

在此方法后,调用了

private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
if (promise == null) {
return;
}
boolean active = isActive();
boolean promiseSet = promise.trySuccess();
if (!wasActive && active) {
pipeline().fireChannelActive();
}
if (!promiseSet) {
close(voidPromise());
}
}

这个方法是在connect方法后调用的,它会调用pipeline的fireChannelActive方法,这个方法实际是把socket连接成功的事件发送出去,这个方法就是Inbound事件的起点。调用fireChannelActive方法后就产生了一个ChannelActive Inbound 事件。
看看fireChannelActive方法:

@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}

注意看看在这个方法中传入的对象是head对象,这就展示了Inbound事件在Pipeline中传输的起点是从head开始的。
AbstractChannelHandlerContext的invokeChannelActive:

static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}

调用head的invokeChannelActive方法:

private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}


因为HeadContext即使Context又是Handler,所以在HeadContext重写了handler()方法,因为这个的handler()返回的就是HeadContext。
看看HeadContext的channelActive

public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();

readIfIsAutoRead();
}

ctx.fireChannelActive()这个方法很明显了,就是把ChannelActive的Inbound事件在Pipeline中传下去。这样会调用到AbstractChannelHandlerContext里面

public ChannelHandlerContext fireChannelActive() {
invokeChannelActive(findContextInbound());
return this;
}

看到findContextInbound方法没!这里就是寻找第一个属性inbound为true的Context,

private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}

然后调用invokeChannelActive方法,这样又会调到

static void invokeChannelActive(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelActive();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelActive();
}
});
}
}

在((ChannelInboundHandler) handler()).channelActive(this);中,如果用户没有重写 channelActive方法, 那么会调用ChannelInboundHandlerAdapter的channelActive方法。

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}

这里面也是仅仅调用了ctx.fireChannelActive方法。这样就是一个循环了。
Context.fireChannelActive-->Connect.findContextInbound--> nextContext.invokeChannelActive-->nextHandler.channelActive--> nextContext.fireChannelActive
在这里循环的最后到达了tail,它实现了ChannelInboundHandler和ChannelHandlerContext 口,当channelActive消息传递到tail后,handler()返回的就是tail本身,来看看tail中的channelActive方法:

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { }

它什么都没有做。
[color=blue]如果是Inbound事件当用户没有实现自定义的处理器时,那么最后默认是不处理的。[/color]

总结:
Outbound:
Outbound事件是请求事件。
Outbound事件的发起者是Channel。
Outbound事件的处理者是unsafe。
Outbound事件在Pipeline中的传输方向是tail-->head。

Inbound:
Inbound事件是通知事件。
Inbound事件发起者是unsafe。
Inbound事件的处理者是Channel。
Inbound事件在Pipeline中传输方向是head-->tail。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值