ChannelPipeline的设计思想
ChannelPipeline是一个双向链表,Channel都有且仅有一个ChannelPipeline与之对应,Channel包含了ChannelPipeline,ChannelPipeline内部包含了N个channelhandler,每一个handler都是由一个线程去执行。
用户可在pipeline中添加多个事件处理器(ChannelHandler),并通过实现ChannelHandler中定义的方法,对回调事件进行定制化的业务处理。ChannelHandler也可以调用自身方法对Channel本身进行操作。
ChannelPipeline的创建
ChannelPipeline的创建是在AbstractChannel的构造方法中创建的,这就意味着每一个AbstractChannel的子类在创建的时候都会创建一个ChannelPipeline,AbstractChannel子类,如下图所示:
类图是删减版的只讲解核心主线,平时常用的就是NioServerSocketChannel,这也说明在ChannelPipeline的创建是在创建NioServerSocketChannel的时候创建的。
对应的源码如下:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
ChannelPipeline的实例化
继续跟踪代码查看ChannelPipeline创建的时候是如何实例化的,源码如下:
//相关变量
final AbstractChannelHandlerContext head;
final AbstractChannelHandlerContext tail;
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
//分别构建Pipeline的头对象和尾对象,TailContext和HeadContext都是AbstractChannelHandlerContext,这里不多做解释
tail = new TailContext(this);
head = new HeadContext(this);
//形成双向链表
head.next = tail;
tail.prev = head;
}
这里需要简单说明一下TailContext和HeadContext的区别,TailContext和HeadContext都继承了AbstractChannelHandlerContext,但是TailContext只实现了ChannelInboundHandler接口,但是HeadContext实现了ChannelInboundHandler和ChannelOutboundHandler接口,
head节点作为pipeline的头结点开始接收并传递inbound事件。并作为pipeline的最后一环最终接收处理outbound事件(委托Unsafe进行outbound事件的相关IO操作)。
tail节点作为pipeline的第一环传递outbound事件,其实就是将outbound事件透传到前一个outbound处理节点。并作为pipeline的最后一环最终接收inbound事件,大部分左右是终止inbound事件的传播。
ChannelInitializer 的添加
到目前为止,这个 Pipeline 还并不能实现什么特殊的功能,因为head和tail并没有实际的功能,通常来说,我们在初始化 ServerBootstrap,会添加我们自定义的 ChannelHandler, 以服务端启动代码片段来举例:
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
// 使用NioServerSocketChannel作为服务器的通道实现
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//对workerGroup的SocketChannel设置处理器
ch.pipeline().addLast(new NettyServerHandler());
}
});
在调用 handler 时,传入了一个 ChannelInitializer 对象,它提供了一个 initChannel()方法给我们初始化 ChannelHandler,init执行如下所示:
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//从ChannelPipeline 中获取ChannelPipeline
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}
//先添加配置相关的ChannelHandler
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
//添加handler
pipeline.addLast(handler);
}
//执行childHandler的init方法,把自定义的handle添加到ChannelPipeline 中
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
到这里ChannelInitializer 执行就算完成了, config.handler()返回的 ChannelHandler 添加到 Pipeline 中,而 handler()返回的其实就是我们在初始化 Bootstrap 时通过 handler()方法设置的 ChannelInitializer 实例(ChannelInitializer 实现了 ChannelInboundHandlerAdapter),因此这里其实就是将 ChannelInitializer 插入到了 Pipeline 的末端,到这些说明了为什么我们需要先配置ChannelInitializer ,和ChannelInitializer 中initChannel的主要功能。
ChannelHandler的调用原理
ChannelHandler调用是在ServerBootstrap.bind()调用的时候,执行initAndRegister();层层调用到AbstractChannel.AbstractUnsafe#register0方法,源码如下:
//第一步
//AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
doRegister();//将SocketChannel注册到selector中
pipeline.fireChannelRegistered();//重点分析
}
//第二步
//DefaultChannelPipeline#fireChannelRegistered
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);//传入head,开始执行
return this;
}
//第三步
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
//判断时候在EventLoop()中,如果没有开启线程异步执行
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
//第四步
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
//第五步
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
//第六步
@Skip
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelRegistered();
}
//第七步
@Override
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
return this;
}
再继续执行就会进入循环,一直执行第三步->第七步。而退出逻辑在第七步中的
findContextInbound(MASK_CHANNEL_REGISTERED)方法,源码如下:
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
//判断ctx的执行标志是不是Inbound,是的话结束循环,依次执行所有的ChannelHandler的channelRegistered方法
} while ((ctx.executionMask & mask) == 0);
return ctx;
}
这里需要注意得是head和tail的channelRegistered的实现,如下所示:
//head的,直接调用了下一个channelHandle的ChannelRegistered();方法
@OverridechannelRegistered,直接调用了下一个channelHandle的ChannelRegistered();方法
public void channelRegistered(ChannelHandlerContext ctx) {
invokeHandlerAddedIfNeeded();
ctx.fireChannelRegistered();
}
//tail的,没有任何的执行代码,说明到这里整个链条执行完成。当然channelRegistered只会在bind()方法中的initAndRegistered()的时候调用
@Override
public void channelRegistered(ChannelHandlerContext ctx) { }
head的,直接调用了下一个channelHandle的ChannelRegistered();方法
tail的,没有任何的执行代码,说明到这里整个链条执行完成。当然channelRegistered只会在bind()方法中的initAndRegistered()的时候调用。
PS:在ChannelHandler执行的时候,无论是任何的IO操作,如果想要链式执行必须调用对应的ctx.fireChannelXX()方法,不管是channelRegistered,还是channelActive,或者channelRead,否则都会在当前ChannelHandler中结束,不在执行下一个ChannelHandler对应的方法。
ChannelHandler添加原理
上面的流程我们知道ChannelHandler执行的时候,会从 head 开始遍历 Pipeline 的双向链表,然后找到第一个属性 inbound 为 true 的 ChannelHandlerContext 实例,而我们代码中配置完成之后,只有一个 ChannelInboudHandler,就是ChannelInitializer ,在ChannelInitializer 中的hannelRegistered()方法没有执行之前,Pipeline的里面的链表内容如下:
ChannelInitializer的注册方法如下:
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
// the handler.
//核心方法
if (initChannel(ctx)) {
// we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
// miss an event.
ctx.pipeline().fireChannelRegistered();
// We are done with init the Channel, removing all the state for the Channel now.
removeState(ctx);
} else {
// Called initChannel(...) before which is the expected behavior, so just forward the event.
ctx.fireChannelRegistered();
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) { // Guard against re-entrance.
try {
//执行重写的initChannel方法
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 {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
注意initChannel()方法中调用了initChannel(© ctx.channel()),这个initChannel(C ch)方法我们也很熟悉,它就是我们在初始化 Bootstrap 时,调用 handler 方法传入的匿 名内部类所实现的方法:
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//对workerGroup的SocketChannel设置处理器
ch.pipeline().addLast(new NettyServerHandler());
}
});
当我们调用完这个方法之后, 我们自定义的 ChannelHandler 就插入到了 Pipeline,此时 Pipeline 的状态如下图所示:
当添加完成自定义的 ChannelHandler 后,在 finally 代码块会删除自定义的 ChannelInitializer,也就是 remove(ctx)最 终调用 ctx.pipeline().remove(this),因此最后的 Pipeline 的状态如下:
Pipeline中的addLast方法
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
//添加ChannelHandler 到链表中
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventLoop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
//分析这段代码可以知道addLast每次都把新的channelhandle添加到tail的前面
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
Pipeline 的事件分类
1、Inbound事件
用于描述因外部事件导致的Channel状态变更。
2、Outbound事件
用于定义Channel能够提供的IO操作。
Pipeline的事件处理机制
Unsafe介绍
Unsafe是Channel的内部类,一个Channel对应一个Unsafe。
Unsafe用于处理Channel对应网络IO的底层操作。ChannelHandler处理回调事件时产生的相关网络IO操作最终也会委托给Unsafe执行。
在Netty中socket的相关操作,包括SocketAddress获取、selector注册、网卡端口绑定、socket建连与断连、socket写数据。这些操作都是有Unsafe来实现完成的,之后Unsafe在调用对应的 pipeline.fireXXX();方法进行事件的处理。
NioByteUnsafe实现了与socket连接的字节数据读取相关的操作。
NioMessageUnsafe实现了与新连接建立相关的操作。
inbound事件处理
fireChannelRead的执行流程如上图所示,fireChannelActiv、fireChannelReadComplete等inbound事件的处理过程可以自己跟代码看一下。(head节点对channelRead事件向后透传。对channelActive和channelReadComplete事件,向后传递并产生了Channel.read()的outbound事件)。
outbound事件处理的事件处理针对的是bind、connect、read、writeAndFlush等outbound事件的处理过程,这里不做详解,但是这些操作最后都是委托给DefaultChannelPipeline中的HeadContext的IO操作方法的方法,源码如下:
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
unsafe.bind(localAddress, promise);
}
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) {
unsafe.connect(remoteAddress, localAddress, promise);
}
@Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
unsafe.disconnect(promise);
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
unsafe.close(promise);
}