Netty source code analysis server Channel series register

Fanger Wei code or search public micro-channel number of the next scan 菜鸟飞呀飞, you can focus on micro-channel public number, read more Spring源码分析, and Java并发编程articles.

Micro-channel public number

problem

  1. In the wording of the JDK native NIO by serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT)the server channel registered on the multiplexer selector, then in Netty, but also how to NioServerSocketChannel registered on the multiplexer it? During the registration process, Netty has done what extra things?
  2. In the previous article Netty series of source code analysis server Channel initialization , the analysis in the init (channel) method, added to the pipeline an anonymous class: ChannelInitializer, the anonymous class initChannel (channel) method, executed it is important logic: add two handler in the pipeline. But the article is a direct say initChannel (channel) of the results, did not say how the code is a callback to the anonymous class initChannel on (channel) method, the following article will explain this in more detail.

Part Review

When invoked ServerBootstrap.bind(port), the code to perform AbstractBootstrap.doBind(localAddress)the method, in turn calls doBind () method initAndRegister()Method. code simplified initAndRegister () method is as follows.

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        /**
         * newChannel()会通过反射创建一个channel,反射最终对调用到Channel的构造器
         * 在channel的构造器中进行了Channel很多属性的初始化操作
         * 对于服务端而言,调用的是NioServerSocketChannel的无参构造器。
         */
        channel = channelFactory.newChannel();
        /**
         * 初始化
         * 调用init方法后,会想服务端channel的pipeline中添加一个匿名类,这个匿名类是ChannelInitializer
         * 这个匿名类非常重要,在后面channel的register过程中,会回调到该匿名类的initChannel(channel)方法
         */
        init(channel);
    } catch (Throwable t) {
        // 省略部分代码...
    }
    ChannelFuture regFuture = config().group().register(channel);
    // 省略部分代码...
    return regFuture;
}
复制代码

initAndRegister()The method of action is to initialize the server channel, i.e. NioServerSocketChannel, and registered on the server channel multiplexer. In the previous article Netty series of source code analysis server Channel initialization analyzed initAndRegister in () method of the first half, that is, the server channel initialization process. During the service side channel initialization by channelFactory.newChannel()NioServerSocketChannel no arguments to the constructor call will be reflected, it will eventually create a NioServerSocketChannel instance, in the configuration process will initialize NioServerSocketChannel many attributes, for example: pipeline, unsafe. Then call the init(channel)method, will be set up for the NioServerSocketChannel options、attrand other properties, the most important is the addition of a ChannelInitinalizer type of anonymous classes in the pipeline NioServerSocketChannel.

After one complete the init (Channel) method, and then executes the code to ChannelFuture regFuture = config().group().register(channel);. The analysis focuses on this line of code is this article today, and its main function is to register with the server Channel multiplexer.

register (channel) source

ChannelFuture regFuture = config().group().register(channel);
复制代码

In this line of code, config()you get to that ServerBootstrapConfig object that holds some of the properties we have configured the server to Netty, such as setting the bossGroup, workerGroup, option, handler, childHandler other attributes are stored in ServerBootstrapConfig this object. (BossGroup, workerGroup represents the main Reactor NioEventLoopGroup from the thread pool, which is what we created through the following code)

// 负责处理连接的线程组
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 负责处理IO和业务逻辑的线程组
NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);
复制代码

config().group()BossGroup we get to that set for the server. So config().group().register(channel)actually calls NioEventLoopGroup the register (channel) method. Since NioEventLoopGroup inherited MultithreadEventLoopGroup, register (channel) defined in MultithreadEventLoopGroup class, it will call MultithreadEventLoopGroup.register(channel). (Netty class inheritance is very complex, so look at the source code in the process, the best way to see the use of IDEA debug source code, or sometimes do not know exactly the specific implementation of a method in which the class).

Source MultithreadEventLoopGroup.register (channel) method is as follows.

public ChannelFuture register(Channel channel) {
    return next().register(channel);
}
复制代码

NioEventLoopGroup is a thread group, which contains a set of NioEventLoop. next()NioEventLoopGroup this way is removed from the thread group by a polling method NioEventLoop (NioEventLoop be understood that it is simply a thread), then performs register (channel) through NioEventLoop.

When you call NioEventLoop.register(channel), the call is actually SingleThreadEventLoop class register (channel). SingleThreadEventLoop.register (channel) as source.

public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}
复制代码

At this point the channel is NioServerSocketChannel, this is NioEventLoop. ChannelPromise create an object, and then save the channel and NioEventLoop ChannelPromise to this object, it is easy to get back in the channel and from ChannelPromise NioEventLoop.

Followed by another call is in register SingleThreadEventLoop (channelPromise) overloads. Source follows.

public ChannelFuture register(final ChannelPromise promise) {
    ObjectUtil.checkNotNull(promise, "promise");
    /**
     *  对于服务端而言
     *  promise是DefaultChannelPromise
     *  promise.channel()获取到的是NioServerSocketChannel
     *  promise.channel().unsafe()得到的是NioMessageUnsafe
     *  由于NioMessageUnsafe继承了AbstractUnsafe,所以当调用unsafe.register()时,会调用到AbstractUnsafe类的register()方法
     */
    // this为NioEventLoop
    promise.channel().unsafe().register(this, promise);
    return promise;
}
复制代码

In the register (final ChannelPromise promise), the promise.channel()acquired is NioServerSocketChannel (As previously mentioned, when you create ChannelPromise, NioServerSocketChannel will be saved to the promise, so can get to here).

promise.channel().unsafe()In fact NioServerSocketChannel.unsafe(), it gets to that NioMessageUnsafeobject. When the object is to save it to NioServerSocketChannel it? When the reflective NioServerSocketChannel constructor call, the constructor NioServerSocketChannel the parent class is initialized unsafe properties for the server channel concerned, is unsafe NioMessageUnsafe instance object attributes; the client channel concerned, NioSocketChannelUnsafe property instance is unsafe objects (remember that this is important, access behind the new connection, read and write data are based on these two Unsafe achieved).

Because here is the server channel, it promise.channel().unsafe().register(this, promise)is actually a call to register (this, promise) method NioMessageUnsafe class. NioMessageUnsafe inherited AbstractUnsafeclass register(this, promise)method is actually defined in AbstractUnsafe class, NioMessageUnsafe class does not override this method hair, so the final will be called to AbstractUnsafe.register(this, promise). And finally to the core code! ! !

AbstractUnsafe.register(this, promise)The method of the deletion source as the source.

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    // 省略部分代码....

    // 对服务端channel而言,这一步是给NioServerSocketChannel的eventLoop属性赋值
    AbstractChannel.this.eventLoop = eventLoop;

    // 判断是同步执行register0(),还是异步执行register0()
    if (eventLoop.inEventLoop()) {
        // 同步执行
        register0(promise);
    } else {
        try {
        	// 提交到NioEventLoop线程中,异步执行
            eventLoop.execute(new Runnable() {
                @Override
                public void run() {
                    register0(promise);
                }
            });
        } catch (Throwable t) {
            // 省略部分代码
        }
    }
}
复制代码

Logic code above, can be divided into two parts. First: by AbstractChannel.this.eventLoop = eventLoopto eventLoop property NioServerSocketChannel assignment, so the service side of the channel back to bind on this NioEventLoop, all the operations performed by this thread. Second: By eventLoop.inEventLoop()judged is synchronous register0 () method, or asynchronous execution register0 () method.

eventLoop.inEventLoop()Logic is relatively simple, is determined and stored in the current thread eventLoop thread are equal, if equal, synchronous execution register0 (); if not equal, to the asynchronous execution register0 (). This time due to the current thread is the main () thread, certainly not equal eventLoop in the thread, so will be executed asynchronously register0 by eventLoop ().

So far, still did not see the server-side code to bind to the channel multiplexer. Thus, the binding operation should register0 (), the following look register0 () code.

private void register0(ChannelPromise promise) {
    try {
        // 省略部分代码...
        boolean firstRegistration = neverRegistered;
        /**
         * 对于服务端的channel而言,doRegister()方法做的事情就是将服务端Channel注册到多路复用器上
         */
        doRegister();
        neverRegistered = false;
        registered = true;

        //会执行handlerAdded方法
        pipeline.invokeHandlerAddedIfNeeded();

        safeSetSuccess(promise);
        //通过在pipeline传播来执行每个ChannelHandler的channelRegistered()方法
        pipeline.fireChannelRegistered();

       	// 如果服务端channel已经激活,就执行下面逻辑。
       	// 由于此时服务端channel还没有绑定端口,因此isActive()会返回false,不会进入到if逻辑块中
        if (isActive()) {
            // 省略部分代码
        }
    } catch (Throwable t) {
        // 省略部分代码...
    }
}
复制代码

In register0 () method, the important logical three steps, the first: doRegister (); Second: pipeline.invokeHandlerAddedIfNeeded (); Third: pipeline.fireChannelRegistered (). Here are three steps to take a look at what things are done.

doRegister(). doRegister () is the real end of the service channel registered to a step in the multiplexer. the doRegister () call is the doRegister AbstractNioChannel class () method, the following source deletion.

protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException e) {
            // 异常处理......
        }
    }
}
复制代码

Where the javaChannel()acquisition is JDK ServerSocketChannel Central students.

eventLoop().unwrappedSelector()JDK is acquired natively multiplexer Selector (the underlying data structure is replaced). (UnwrappedSelector EventLoop property is created when NioEventLoop, initialization, the underlying data structure is to be replaced at this time)

So javaChannel().register(eventLoop().unwrappedSelector(), 0, this)this line of code actually calls the JDK native ServerSocketChannel the register(selector,ops,attr)method, and then register the server to the multiplexer Channel Selector.

Note that when invoking native JDK register () method, the third parameter is passed in this, is the current subject NioServerSocketChannel this case represented. This will save as an attachment to the multiplexer Selector, the benefits of doing so is that the back channel multiplexer Selector can get to the end of the service. The second parameter passed is 0, indicating that this time will be registered on the service side channel multiplexer, event identifier interested in server chennel is 0, that is not interested at this time in any event. (Really began to be interested in receiving events after the listening port on the server channel).

When doRegister () method been executed, it will perform the second step: pipeline.invokeHandlerAddedIfNeeded(). This step is to do a callback handler in the pipeline handlerAdded()method. Source invokeHandlerAddedIfNeeded () method is as follows.

final void invokeHandlerAddedIfNeeded() {
    assert channel.eventLoop().inEventLoop();
    if (firstRegistration) {
        firstRegistration = false;
        // 只有在第一次注册时候才会执行这儿的逻辑
        // 回调所有Handler的handlerAdded()方法
        callHandlerAddedForAllHandlers();
    }
}
复制代码

Only when the first registration when channel, will perform the callHandlerAddedForAllHandlers()method. The core logic callHandlerAddedForAllHandlers()on.

private void callHandlerAddedForAllHandlers() {
    final PendingHandlerCallback pendingHandlerCallbackHead;
    synchronized (this) {
        assert !registered;

        // 该通道本身已注册。
        registered = true;

        pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
        this.pendingHandlerCallbackHead = null;
    }
    // pendingHandlerCallbackHead属性的值是什么时候被初始化的?
    PendingHandlerCallback task = pendingHandlerCallbackHead;
    while (task != null) {
        task.execute();
        task = task.next;
    }
}
复制代码

From callHandlerAddedForAllHandlers()that we can find the source code of the method, its core logic is this while(task!=null)cycle, then perform task recycle. task is the initial value this.pendingHandlerCallbackHead, i.e. DefaultChannelPipeline.pendingHandlerCallbackHead. So the question is, what is the value of the property when pendingHandlerCallackHead initialized. (Then it will begin to force the ignorant, the code will be a variety of jumps, this time it will play the debug function of the IDEA)

In the initAndRegister()process, calls the init(channel)method, () adds a ChannelInitializer type of anonymous classes to the pipeline by pipeline.addLast in init (channel) method, in this paper ”上篇回顾“this section mentions that this step is very important, now it is at just how important.

When calling pipeline.addLast () method calls to eventually DefaultChaannelPipeline the addLast(EventExecutorGroup group, String name, ChannelHandler handler)method, the source code of the method are as follows.

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(handler);
        newCtx = newContext(group, filterName(name, handler), handler);
        addLast0(newCtx);

        // 默认为false,当为false时,表示的channel还没有被注册到eventLoop中
        if (!registered) {
            //判断handlerState属性等于0  并且设置为1
            // pending:待定,听候
            newCtx.setAddPending();
            // 将ChannelHandlerContext封装成一个PendingHandlerCallback(实际上就是一个Runnable类型的Task)
            // 然后添加到回调队列中
            callHandlerCallbackLater(newCtx, true);
            return this;
        }

        //返回NioEvenGroup
        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
            callHandlerAddedInEventLoop(newCtx, executor);
            return this;
        }
    }
    callHandlerAdded0(newCtx);
    return this;
}
复制代码

In the above code, addLast0(newCtx)it is to really add to the handler in the pipeline, but after completion of the addition, but also to perform a method: callHandlerCallbackLater(newCtx, true). The method name translation is: much later callback method callback handler's. Look below callHandlerCallbackLater(newCtx, true)the specified logical methods.

private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
    assert !registered;
    // added为true
    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;
    }
}
复制代码

Parameters ctx is what is it? It is in front of ChannelInitializer this class we create an anonymous package into AbstractChannelHandlerContext objects; incoming parameters added at this time is true. So create out of the task Shi PendingHandlerAddedTask(ctx), then we can find out the last assignment task to create a pendingHandlerCallbackHead property.

Back to the previous callHandlerAddedForAllHandlers()method, we know that the value is PendingHandlerAddedTask pendingHandlerCallbackHead property (ctx), so execution task.execute()time is execution execute PendingHandlerAddedTask object (). In the execute () method calls to the callHandlerAdded0(ctx)method, then the call ctx.callHandlerAdded(), the object is ChannelInitializer CTX anonymous classes as objects of AbstractChannelHandlerContext. Continue to follow up ctx.callHandlerAdded()method of source code, eventually found, it eventually calls handlerAdded () method handler object. Here, finally found the handlerAdded()method is where the callback. So far pipeline.invokeHandlerAddedIfNeeded()the method final execution finished.

Back register0 () method, when pipeline.invokeHandlerAddedIfNeeded()the method execution is completed, then execution down to code execution pipeline.fireChannelRegistered(), which is the third step we mentioned earlier. This step to do is spread Channel register an event, how it spread? Is the beginning of the first node along the pipeline in the handler, the handler next sequentially performed for each channelRegistered()method.

Through a follow-up step by step, we can see pipeline.fireChannelRegistered()the final calls to AbstractChannelContextHandler object invokeChannelRegistered(). The source of the method are as follows.

private void invokeChannelRegistered() {
    if (invokeHandler()) {
        try {
            // handler()就是获取handler对象
            ((ChannelInboundHandler) handler()).channelRegistered(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        fireChannelRegistered();
    }
}
复制代码

Where the handler () Gets the current AbstractChannelContextHandler object is wrapped handler objects, such as you created earlier ChannelInitializer的匿名类. Then call handler object channelRegistered(this)method, so the final calls to ChannelInitializer的匿名类the channelRegistered(this)method. Here we look at the anonymous class ChannelInitializer channelRegistered(this)method has done what to do.

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    // Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
    // the handler.
    // 主要看initChannel(ctx)方法的逻辑
    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();
    }
}
复制代码

You can see, it will first call the initChannel(ctx)method, and then call ctx.pipeline().fireChannelRegistered()or ctx.fireChannelRegistered(), behind these two methods you can see by the method name, it is to continue to spread channelRegistered execution handler in the pipeline () method, you can not look up. Look at the focus initChannel (ctx) method here. Note: Parameter Type initChannel here (ctx) method is the ChannelHandlerContexttype, there will be a initChannel (channel) method in the back, it is the parameter type Channel, specifically reminded about here, do not confuse the two overloaded methods.

Source initChannel (ctx) method is as follows.

private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (initMap.add(ctx)) { // Guard against re-entrance.
        try {
            //ChannelInitializer匿名类的initChannel(channel)的方法 将在这里被调用
            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;
}
复制代码

It can be seen in initChannel (ctx) method, first call the initChannel (channel) method. Since ChannelInitializer anonymous class created earlier rewrite initChannel (channel) method, so this time will be called to rewrite the initChannel (channel) method. For easy viewing, the following ChannelInitializer anonymous code when creating class instances given by the shots.

initChannel(channel)

In rewriting initChannel (channel) method, we can see that we added Xianxiang pipeline in main () is provided for the handler server, and then by a ch.eventLoop().execute()line of code in an asynchronous manner to the pipeline added ServerBootstrapAcceptor type of handler, the handler is behind ServerBootstrapAcceptor responsible for client access to the handler, is very important.

Here can finally sigh, the second question at the beginning of the article: When callback intiChannel (channel), and now finally know.

However, it is not over yet. Back initChannel (ctx) method, we found that in the finally block, made a very important operation, that is, the handler ChannelInitializer anonymous class represents, removed from the pipeline. This is because the purpose of this anonymous class existence, is to end in the service channel initialization and registration process, the structure of its initialization pipeline, and now the server channel initialization and registration has been completed, and the server channel will only when the service starts initialization time, so do not face the back of the anonymous existence meaning, so as to be removed from the pipeline, the channel configuration diagram of the server in the final pipeline follows.

pipeline structure of FIG.

So far, initAndRegister () method of analysis finally finished.

to sum up

  • This paper analyzes the second half of the initAndRegister () method, i.e., registration server to process channel multiplexer, which is the final call of the JDK NIO packages, register ServerSocketChannel the method, the registration server to the multi-channel Selector on the multiplexer.
  • Then article also follow step by step through the code, a detailed description ChannelInitializer anonymous class instances initChannel (channel) is the callback process, eventually forming a pipeline configuration of the server in the channel.
  • So far, Netty server channel initialization and registration has been completed, but the server startup process is not over, leaving the final step: binding server channel and port number, port binding process, the next article analysis.

recommend

Micro-channel public number

Guess you like

Origin juejin.im/post/5df50f5851882512523e7ba4