netty源码分析之-Channel注册流程详解(8)

来源:互联网 发布:外卖软件下载 编辑:程序博客网 时间:2024/06/05 16:48

前面分析来Channel初始化的流程,在这里继续分析Channel注册的相关流程,在netty中关于register底层实际上就是java nio中将Channel注册到Selector上的过程

对于register相关代码分析:

final ChannelFuture 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);        }        ChannelFuture regFuture = config().group().register(channel);  ①        if (regFuture.cause() != null) {            if (channel.isRegistered()) {                channel.close();            } else {                channel.unsafe().closeForcibly();            }        }        return regFuture;    }

对于①关于register的核心代码分析,config()返回的是和bootstrap相关的配置文件,group返回的是对应的NioEventLoopGroup,register方法实现:

    //MultithreadEventLoopGroup中的:    @Override    public ChannelFuture register(Channel channel) {        return next().register(channel);    }

对于next方法实现:

//MultithreadEventLoopGroup中的:    @Override    public EventLoop next() {        return (EventLoop) super.next();    }//父类EventExecutorChooserFactory中实现为    @UnstableApi    interface EventExecutorChooser {        /**         * Returns the new {@link EventExecutor} to use.         */        EventExecutor next();    }

也就是返回下一个EventExecutor

继续分析register方法实现:

    //在SingleThreadEventLoop中实现:    @Override    public ChannelFuture register(Channel channel) {        return register(new DefaultChannelPromise(channel, this));    }

DefaultChannelPromise为future相关的对象。继续追溯到底层实现:

    ////在SingleThreadEventLoop中实现:    @Override    public ChannelFuture register(final ChannelPromise promise) {        ObjectUtil.checkNotNull(promise, "promise");        promise.channel().unsafe().register(this, promise);        return promise;    }

ChannelPromise为可写的ChannelFuture对象,并且可以获得相关的Channel对象

    //AbstractNioChannel中实现:    @Override    public NioUnsafe unsafe() {        return (NioUnsafe) super.unsafe();    }

Channel中Unsafe接口下定义的都是底层相关的方法定义

//AbstractUnsafe类实现了上述的Unsafe定义的register方法:public final void register(EventLoop eventLoop, final ChannelPromise promise) {            ...            AbstractChannel.this.eventLoop = eventLoop;            if (eventLoop.inEventLoop()) {                register0(promise);            } else {                try {                    eventLoop.execute(new Runnable() {                        @Override                        public void run() {                            register0(promise);                        }                    });                } catch (Throwable t) {                    logger.warn(                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",                            AbstractChannel.this, t);                    closeForcibly();                    closeFuture.setClosed();                    safeSetFailure(promise, t);                }            }        }

对于eventLoop.inEventLoop()的实现:

    //判断当前调用的线程是否是EventLoop中维护的那一个线程    public boolean inEventLoop(Thread thread) {        return thread == this.thread;    }

如果调用该方法的线程是EventLoop的那一个线程,那么直接执行,否则提交该任务至EventLoop中的线程来执行。因此可以避免产生多线程并发问题

对于最底层register相关的实现为:

//在AbstractNioChannel中的:protected void doRegister() throws Exception {        boolean selected = false;        for (;;) {            try {                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);                return;            } catch (CancelledKeyException e) {                if (!selected) {                    // Force the Selector to select now as the "canceled" SelectionKey may still be                    // cached and not removed because no Select.select(..) operation was called yet.                    eventLoop().selectNow();                    selected = true;                } else {                    // We forced a select operation on the selector before but the SelectionKey is still cached                    // for whatever reason. JDK bug ?                    throw e;                }            }        }    }

其中:

private final SelectableChannel ch;...protected SelectableChannel javaChannel() {        return ch;    }...private Selector unwrappedSelector;

底层也就是java nio中的将Channel注册到Selector中

需要注意:

  • 一个EventLoopGroup当中会包含一个或多个EventLoop
  • 一个EventLoop在它的整个生命周期当中都只会与唯一一个Thread进行绑定
  • 所有的由EventLoop所处理的各种I/O事件都将在它所相关联的那个Thread上进行处理(因此在实现Handle时候无需关心线程安全性问题)
  • 一个Channel在它的整个生命周期中只会注册在一个EventLoop
  • 一个EventLoop在运行过程当中,会被分配给一个或者多个Channel

    重要结论:在netty中,Channel的实现一定是县城安全的,基于此,我们可以存储一个Channel的引用,并且在需要向远程端点发送数据时,通过这个引用来调用Channel相应的方法,即便当时有很多线程都在使用它也不会出现多线程问题,而且消息一定会按照顺序发送出去(底层是用队列来维护提交的任务)。


所以我们在业务开发中,不要将长时间执行的耗时任务放入EventLoop的执行队列中(执行的Channelhandle那些回调方法),因为它将会一直阻塞该线程所对应的所有Channel上的其他执行任务,如果我们需要进行阻塞调用或是耗时的操作(实际开发中很常见),那么我们就需要使用一个专门的EventExecutor(业务线程池)。

这里的业务线程池有两种方式能够实现:
  • 第一种是在ChannelHandler的的回调方法中,直接使用线程池,将需要执行的业务任务提交到线程池中。
  • 第二种是借助于Netty提供的向ChannelPipeline添加ChannelHandler时调用的类似于addLast方法来传递EventExecutor
    默认情况下,调用pipeline.addLast(new MyServerHandler()),ChannelHandler中的回调方法都是由I/O线程所执行;如果调用ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler… handlers),那么ChannelHandler中的回调方法就可以由参数中的group线程组来执行
阅读全文
0 0
原创粉丝点击