系列文章目录
Netty源码学习 - netty服务端通道的实例化流程
Netty源码学习 - netty服务端通道的初始化流程
Netty源码学习 - netty服务端通道的注册流程
文章目录
前言
在上一章中我们介绍了netty通道的实例化过程,在通过ServerBootStrap进行端口绑定io.netty.bootstrap.AbstractBootstrap#bind(int)
的时候,首先就会执行初始化和注册,其中首先就是执行netty中通道的实例化,对应的代码为
channel = channelFactory.newChannel()
此时会通过反射实例化用户通过io.netty.bootstrap.AbstractBootstrap#channel
方法设置的通道类,比如NioServerSocketChannel类的默认构造就涉及到了获取nio的通道类以及socket,同时每个netty的通道类又包含了管道实例pipeline,而管道实例pipeline当中又包含了了一个双向链表,在pipeline实例化的过程中会创建这个双向链表的头结点(HeadContext)和尾结点(TailContext)。对应NIO,后面还会设置非阻塞模式、创建接收缓存分配器、缓存分配器。因此,netty通过io.netty.bootstrap.AbstractBootstrap#channel
设置不同的通道类然后在构造中实现不同的逻辑屏蔽了BIO、NIO的不同。对于用户来说,只要设置netty的不同通道类就可以选择使用BIO还是NIO了。在实例化Netty的通道类之后,接下来就是初始化操作了。对应源码(io.netty.bootstrap.AbstractBootstrap#initAndRegister
)为
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
提示:以下是本篇文章正文内容,下面案例可供参考
一、设置参数
在netty中,用户可以通过io.netty.bootstrap.AbstractBootstrap#option
设置和移除ChannelOption
信息,对应的接口如下
/**
* Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
* created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
*/
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return self();
}
或者通过以下接口设置或移除参数
/**
* Allow to specify an initial attribute of the newly created {@link Channel}. If the {@code value} is
* {@code null}, the attribute of the specified {@code key} is removed.
*/
public <T> B attr(AttributeKey<T> key, T value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
synchronized (attrs) {
attrs.remove(key);
}
} else {
synchronized (attrs) {
attrs.put(key, value);
}
}
return self();
}
在设置的阶段只是记录到AbstractBootstrap中到了两个属性当中
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
在创建好netty的通道之后,首先需要处理的就是这些选项和属性值。
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 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(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
其中setChannelOption无非就是遍历之前设置好的选项值,然后设置到ChannelConfig当中。比如说io.netty.channel.socket.DefaultServerSocketChannelConfig#setOption的实现如下
@Override
public <T> boolean setOption(ChannelOption<T> option, T value) {
validate(option, value);
if (option == SO_RCVBUF) {
setReceiveBufferSize((Integer) value);
} else if (option == SO_REUSEADDR) {
setReuseAddress((Boolean) value);
} else if (option == SO_BACKLOG) {
setBacklog((Integer) value);
} else {
return super.setOption(option, value);
}
return true;
}
@Override
public ServerSocketChannelConfig setBacklog(int backlog) {
if (backlog < 0) {
throw new IllegalArgumentException("backlog: " + backlog);
}
this.backlog = backlog;
return this;
}
二、设置通道初始化器ChannelInitializer
在netty当中如果需要给ServerBootstrap添加业务的通道管理器,必须通过ServerBootstrap#childHandler
接口,但是这个接口只接收一个参数,但是实际场景是需要多个通道管理器的,所以netty提供了ChannelInitializer
,其实这个通道初始化器也是一个通道管理器。通过这个通道初始化器来传入多个通道管理器。
官方对这个通道初始化器的说明如下:
A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its EventLoop. Implementations are most often used in the context of AbstractBootstrap.handler(ChannelHandler) , AbstractBootstrap.handler(ChannelHandler) and ServerBootstrap.childHandler(ChannelHandler) to setup the ChannelPipeline of a Channel.
一个特殊的ChannelInboundHandler,提供了一种轻松的方法来将Channel注册到其EventLoop后对其进行初始化。 实现通常在AbstractBootstrap.handler(ChannelHandler)
,AbstractBootstrap.handler(ChannelHandler)
和ServerBootstrap.childHandler(ChannelHandler)
的上下文中使用,以设置通道的ChannelPipeline。
public class MyChannelInitializer extends ChannelInitializer {
public void initChannel(Channel channel) {
channel.pipeline().addLast("myHandler", new MyHandler());
}
}
ServerBootstrap bootstrap = ...;
...
bootstrap.childHandler(new MyChannelInitializer());
...
Be aware that this class is marked as ChannelHandler.Sharable and so the implementation must be safe to be re-used.
1、添加管道
在设置完用户配置的选项和参数之后,系统会添加一个通道初始化器到通道的管道当中。
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) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
这里通过管道的addLast(io.netty.channel.ChannelHandler...)
实现的。
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
1. 用于检查当前的通道管理器是不是已经加入过管道中,如果已经加入过,但不是可共享的,则会抛出异常 is not a @Sharable handler, so can't be added or removed multiple times.通常这种在多个管道中使用的通道管理器必须保证线程安全。
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
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()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
a、Sharable检查
刚开始学习netty的时候经常会遇到is not a @Sharable handler, so can't be added or removed multiple times.
这样的错误。因为用户设置的通道管理器可能被多个通道共享,如果用户没有显示通过@Sharable
来标识,那么在第二次添加到通道的时候就会抛出这个异常了。这里主要是保证通道管理器的线程安全性。如果对应的通道管理器不是线程安全的,用户可以通过new的方式加入到通道当中,比如
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
以上就是通过new的方式添加的,这样每个管道加入的是不同的实例,不存在线程安全性了, 此时就不需要在通道管理器上面添加@Sharable
注解。线程安全性检查对应的源码如下所示:
private static void checkMultiplicity(ChannelHandler handler) {
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
可以看出,只有在h.added为false或者h.isSharable的情况下才不会抛出异常,handler第一次加入管道的时候added的属性是为false的,满足条件,在checkMultiplicity结尾就会赋值为true,后面都不会再更改了。所以后续加入管道的时候只会检查是不是可共享的通道处理器了。
/**
* Return {@code true} if the implementation is {@link Sharable} and so can be added
* to different {@link ChannelPipeline}s.
*/
public boolean isSharable() {
/**
* Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a
* {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different
* {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of
* {@link Thread}s are quite limited anyway.
*
* See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.
*/
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}
所谓的检查是不是可共享的其实就是看对应的类上面是否包含有@Sharable
注解。比如ChannelInitializer
就是一个线程安全的通道管理器。
b、创建对应通道管理器上下文对象ChannelHandlerContext对象
在netty当中一个通道(Channel
)有一个管道(ChannelPipeline
),管道是用于管理这个通道相关的通道处理器(ChannelHandler
)的。但是在管道当中并不是通过链表保存的通道管理器,而是保存的通道管理器上下文对象(ChannelHandlerContext
)的链表结构,所以在往管道中添加通道管理器的时候首先需要转变为通道管理器上下文。对应源码为
newCtx = newContext(group, filterName(name, handler), handler);
addLast0(newCtx);
- 获取通道管理器上下文名称
要创建通道管理器上下文对象,首先需要一个名称用于区分不同的通道管理器上下文对象。如果用户添加通道管理器的时候传入的名称,则使用用户定义的,但是会检查一下这个名称的唯一性,如果用户没有自定义,则由程序生成一个可用的名称,同样也会检查唯一性。
private String filterName(String name, ChannelHandler handler) {
if (name == null) {
return generateName(handler);
}
checkDuplicateName(name);
return name;
}
自动生成的名称与类的类型简单限定名有关,后缀为#0,但考虑到通道处理器类会被使用多次,所以后缀在检查已经存在之后会变为#1、#2等值。
注意这里的使用多次是指在同一个管道中,检查检查当前的管道的链表中是不是已经存在同名的通道管理器上下文。从头结点开始一个一个遍历,检查上下文的名称。
private AbstractChannelHandlerContext context0(String name) {
AbstractChannelHandlerContext context = head.next;
while (context != tail) {
if (context.name().equals(name)) {
return context;
}
context = context.next;
}
return null;
}
- 创建通道管理器上下文对象
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
其实在添加通道管理器的时候,可以指定EventExecutorGroup
,此处为null,所以先不介绍了。
The EventExecutorGroup is responsible for providing the EventExecutor’s to use via its next() method. Besides this, it is also responsible for handling their life-cycle and allows shutting them down in a global fashion.
创建一个DefaultChannelHandlerContext
对象。
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;
}
首先需要判断是入站处理器还是出站处理器,对应的逻辑非常简单,就是判断实现的接口类型即可。
无论是在父类还是在当前类构造当中都是简单的赋值而已,并没啥复杂的逻辑,创建一个通道管理器上下文对象还是蛮简单的。就是设置名称、对应的管道对象、是否进站处理器、是否出站处理器、对应的执行器(线程池)、通道管理器对象。
那么接下来就需要处理这个上下文对象在管道当中的链接关系了,对应源码为addLast0(newCtx)
。在这里是添加到管道的尾部,但是要注意的是,并不是添加到链表的尾部,而是添加到尾部的前面一个节点,因为在netty当中的管道的首节点和尾节点都是固定的HeadContext和TailContext对象,其他用户定义的通道处理器无论是添加到首部还是尾部,都是在这两个之间的位置。
设置好关系之后如下所示
在这里设置管道的链表上下引用关系并不是线程安全的,但是这里不需要考虑,因为在外面加上了synchronized (this)
保证了只有一个线程操作这个链接关系。
c、注册添加事件
每个管道的都有一个registered
属性用于标识当前的通道是否已经注册过了。
/**
* Set to {@code true} once the {@link AbstractChannel} is registered.Once set to {@code true} the value will never
* change.
*/
private boolean registered;
如果还没被注册过,此时添加通道管理器会注册一个handlerAdded
事件,当通道在户注册的时候触发相应的事件。如果通道已经注册过了,在添加通道管理器,则直接触发。
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
通过CAS操作设置上下文的处理器状态。从初始化状态修改为ADD_PENDING状态。
final void setAddPending() {
boolean updated = HANDLER_STATE_UPDATER.compareAndSet(this, INIT, ADD_PENDING);
assert updated; // This should always be true as it MUST be called before setAddComplete() or setRemoved().
}
private static final AtomicIntegerFieldUpdater<AbstractChannelHandlerContext> HANDLER_STATE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractChannelHandlerContext.class, "handlerState");
private volatile int handlerState = INIT;
/**
* Neither {@link ChannelHandler#handlerAdded(ChannelHandlerContext)}
* nor {@link ChannelHandler#handlerRemoved(ChannelHandlerContext)} was called.
*/
private static final int INIT = 0;
/**
* {@link ChannelHandler#handlerAdded(ChannelHandlerContext)} is about to be called.
*/
private static final int ADD_PENDING = 1;
/**
* {@link ChannelHandler#handlerAdded(ChannelHandlerContext)} was called.
*/
private static final int ADD_COMPLETE = 2;
/**
* {@link ChannelHandler#handlerRemoved(ChannelHandlerContext)} was called.
*/
private static final int REMOVE_COMPLETE = 3;
接下来是要添加一个任务了,因为在一个管道当中可能存在多个事件,这里只会记录一个事件的头部,然后执行的时候,从头部开始遍历,一个一个执行即可。对应的属性为pendingHandlerCallbackHead,主要是出于节省内存的考虑。
/**
* This is the head of a linked list that is processed by {@link #callHandlerAddedForAllHandlers()} and so process
* all the pending {@link #callHandlerAdded0(AbstractChannelHandlerContext)}.
*
* We only keep the head because it is expected that the list is used infrequently and its size is small.
* Thus full iterations to do insertions is assumed to be a good compromised to saving memory and tail management
* complexity.
*/
private PendingHandlerCallback pendingHandlerCallbackHead;
添加任务无非就是往这个对象上面一个一个挂上去即可。因为每个PendingHandlerCallback
对象都有一个next
属性指向下一个PendingHandlerCallback
对象,每一个PendingHandlerCallback
对象只需要关心它的下一个节点即可,不需要单独一个对象来管理所有的PendingHandlerCallback
对象。
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
assert !registered;
PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) {
pendingHandlerCallbackHead = task;
} else {
// Find the tail of the linked-list.
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
callHandlerCallbackLater
这个方法既可以是添加HandlerAdded
事件,也是可以HandlerRemovedTask
事件,取决于传入的参数added
,此处为true,对应的事件为PendingHandlerAddedTask
事件。
private abstract static class PendingHandlerCallback implements Runnable {
final AbstractChannelHandlerContext ctx;
PendingHandlerCallback next;
PendingHandlerCallback(AbstractChannelHandlerContext ctx) {
this.ctx = ctx;
}
abstract void execute();
}
private final class PendingHandlerAddedTask extends PendingHandlerCallback {
PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) {
super(ctx);
}
@Override
public void run() {
callHandlerAdded0(ctx);
}
@Override
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
} else {
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
if (logger.isWarnEnabled()) {
logger.warn(
"Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
executor, ctx.name(), e);
}
remove0(ctx);
ctx.setRemoved();
}
}
}
}
通过以上的步骤之后,pendingHandlerCallbackHead
如下所示。
经过以上步骤之后,通道的初始化流程就完成了。
总结一下,在完成通道实例化之后,就进入了通道的初始化流程,首先会设置各种参数到ChannelConfig
对象当中,然后会给通道添加一个系统定义的ChannelInitializer
对象。由于当前通道还没有在一个eventloop中注册,所以会针对这个ChannelInitializer
对象注册一个未来的事件,当这个通道被注册的时候触发,这个事件就会触发ChannelInitializer
对应的的handlerAdded
事件。就会触发initChannel方法。
/**
* {@inheritDoc} If override this method ensure you call super!
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
initChannel(ctx);
}
}
这个initChannel方法的参数为通道管理器上下文对象,与另一个抽象的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;
}
这个initChannel方法一方面会触发参数为通道实例的initChannel方法,二方面会在执行完触发参数为通道实例的initChannel方法之后移除当前的通道管理器对象,也就是ChannelInitializer
对象,其实这属于ChannelInitializer
区别于用户定义的通道管理器的一个特殊之处:在执行了handlerAdded
事件之后会把自己从管道中移除,这样下一次就不会再触发初始化的事件了。
private void remove(ChannelHandlerContext ctx) {
try {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
} finally {
initMap.remove(ctx);
}
}
其实也就是说,对于通道的初始化事件最终是在通道注册时候回调ChannelInitializer
的io.netty.channel.ChannelInitializer#initChannel(C)
这个方法来实现的。
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
protected abstract void initChannel(C ch) throws Exception;
三、通道初始化器事件
通过以上的分析,其实真正的通道初始化逻辑其实是在
这个匿名的ChannelInitializer
对象中的initChannel方法当中实现的。
在这里主要就是两个流程,一个就是添加用户设置通道处理器,通过
AbstractBootstrap#handler(ChannelHandler)
传入的。第二个就是添加一个ServerBootstrapAcceptor
类型的入站通道处理器。
ServerBootstrapAcceptor
这个类是ServerBootStrap的一个私有静态内部类,只为服务端提供服务。创建这个实例的时候主要涉及到以下几个属性
private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
其中这些参数都是用户自己定义的,比如childGroup在我们实例当中就是如下定义的。
EventLoopGroup workerGroup = new NioEventLoopGroup();
而childHandler则是通过如下方式传入的
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
至于另外两个参数都是可以由用户设置的,如下所示
而这个处理的主要逻辑是在channelRead
方法当中
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
主要的逻辑也就是执行添加handler、设置参数、注册事件触发等逻辑了。当然了以上的逻辑需要等到服务端通道注册之后才会真实触发。