切片Netty-initAndRegister

Netty源码分析基于主流的Netty4.x版本。
继续围绕之前demo,本篇主要看看bind方法里面initAndRegister。

一.doBind流程概述

略过中间跳转,进入AbstractBootstrap的doBind方法

private ChannelFuture doBind(final SocketAddress localAddress) {
		// 初始化配置并注册
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        // 有异常返回
        if (regFuture.cause() != null) {
            return regFuture;
        }
		// 注册初始化执行完成
        if (regFuture.isDone()) {
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // 注册未完成
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 添加监听完成事件
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {	
                	// 有异常封装到promise返回
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        // 注册成功
                        promise.registered();
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

二.initAndRegister

initAndRegister 主要做三件事:

  • 实例化Channel
  • 初始化配置
  • 注册对应的事件
   final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
         	// 这里是NioServerSocketChannel
         	// 对应demo中的channel(NioServerSocketChannel.class)设置
            channel = channelFactory.newChannel();
            // 这里由ServerBootstrap实现
            init(channel);
        } catch (Throwable t) {
          	... 
        }
        ChannelFuture regFuture = config().group().register(channel);
        // 有异常的处理
		...
        return regFuture;
    }

三. 实例化NioServerSocketChannel

上文的channelFactory.newChannel()实例化NioServerSocketChannel

	// 反射实例化,首先调用该无参构造
	public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }

    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        // 用于存储配置信息
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

NioServerSocketChannel 设置了OP_ACCEPT参数,表示它会处理接收事件。

  	protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
        	// channel设置为非阻塞的模式
            ch.configureBlocking(false);
        } catch (IOException e) {
           ...
        }
    }

再往上调用AbstractChannel的构造

   protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        // 创建了DefaultChannelPipeline对象
        pipeline = newChannelPipeline();
  

DefaultChannelPipeline是一个双向链表,有前后指针。这里还创建了两个空的头尾节点。

    protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);

        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

小结下重要地方:

  1. 设置channel对OP_ACCEPT事件感兴趣
  2. 设置channel为非堵塞模式
  3. 实例化了DefaultChannelPipeline,并建立了首尾节点

四. 初始化配置

init 方法主要配置之前demo中添加的配置信息,其中最关键的是添加ServerBootstrapAcceptor。
当前的NioServerSocketChannel只负责接受客户端连接。当有连接进来,会创建一个NioSocketChannel,ServerBootstrapAcceptor会将NioSocketChannel注册到workGroup(专门处理IO事件)中,并将复制这些预设的子group、子handler配置

    @Override
    void init(Channel channel) throws Exception {
    	// 这里的options数据来源于option设置
    	// 例如demo中的option(ChannelOption.SO_BACKLOG, 128)
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
        	// 向NioServerSocketChannel中设置option参数
            setChannelOptions(channel, options, logger);
        }
		// 数据来源attr操作,类似options
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                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;
        // 数据来源类似option操作
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        // 数据来源类似option操作
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }
		
        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方法在ChannelPipeline中做了哪些操作。

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);
			// filterName方法会生成一个不重复的名称,返回ClassName#0的格式
			// 如果有Class名称一致则#后的数字会往上累加至不重复位置
			// 将handler包装到上下文newContext中,后续还会关联executor
            newCtx = newContext(group, filterName(name, handler), handler);
			// 更新双向链表节点指向
            addLast0(newCtx);

            // 首次还未注册,执行上文p.addLast(new ChannelInitializer)进入这里
            if (!registered) {
            	// 设置上下文状态为等待添加
                newCtx.setAddPending();
                // 将任务放入pendingHandlerCallbackHead中稍后在执行
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            // 当前线程是否与NioEventLoop关联线程一致,如果不一致,开启executor对应得出线程执行任务
            if (!executor.inEventLoop()) {
            	// 内部执行executor.execute(()->callHandlerAdded0(newCtx))
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
         // 当前线程与NioEventLoop关联线程一致,直接执行
        callHandlerAdded0(newCtx);
        return this;
    }

executor.inEventLoop()这个是干嘛的?
这里Netty是实现channel操作无锁化串行的关键。将每个channel的操作都关联到对应的线程执行,这样子避免了线程安全问题,而且也避免上下文切换的开销。

到目前为止管道中只有一个处理器
在这里插入图片描述

五. 注册处理器

register调用的是MultithreadEventLoopGroup的方法

	public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }

这里的next()是调用上篇介绍的EventExecutorChooserFactory的方法,实际返回一个NioEventLoop对象。
NioEventLoop是SingleThreadEventLoop的子类,故而register调用的是SingleThreadEventLoop的方法。

	public ChannelFuture register(Channel channel) {
		// channel这里实际是NioServerSocketChannel
        return register(new DefaultChannelPromise(channel, this));
    }

    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

DefaultChannelPromise实际是一个Future对象,Netty对它进行了封装扩展。

绕了这么久,终于来到AbstractChannel的register。

  public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            // 忽略校验
            ...	
            // 关联eventLoop
            AbstractChannel.this.eventLoop = eventLoop;
			// 判断当前线程是否为NioEventLoop关联的线程
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    ...
                }
            }
        }
	private void register0(ChannelPromise promise) {
            try {
            	// 省略校验
                ...
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;
				// 这里执行前文添加的ChannelInitializer里面的方法
                pipeline.invokeHandlerAddedIfNeeded();
				// 修改当前promise状态为成功
                safeSetSuccess(promise);
                // 发布注册成功事件通知,将从header开始流转每一个ChannelInboundHandler处理事件
                pipeline.fireChannelRegistered();
                // 此时的channel其实还未成功激活,下面逻辑不会执行
                if (isActive()) {
                    if (firstRegistration) {
                    	 // 向pipeline传播active事件
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        beginRead();
                    }
                }
            } catch (Throwable t) {
               ...
            }
        }
1.doRegister

这个的实现来自于AbstractNioChannel

   protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
            	// 这里主要注册一个什么都不感兴趣的channel,后续会调整
            	// 一个eventLoop对应一个selector
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                ...
            }
        }
    }

这一步主要是为了获取selectionKey 实例,后续通过interestOps(int ops)修改感兴趣的时间。

2.invokeHandlerAddedIfNeeded

进入invokeHandlerAddedIfNeeded方法

	final void invokeHandlerAddedIfNeeded() {
        assert channel.eventLoop().inEventLoop();
        if (firstRegistration) {
            firstRegistration = false;
            callHandlerAddedForAllHandlers();
        }
    }
    private void callHandlerAddedForAllHandlers() {
        final PendingHandlerCallback pendingHandlerCallbackHead;
       	....
       	// 这里执行了pendingHandlerCallbackHead,之前将ChannelInitializer封装到里面
        PendingHandlerCallback task = pendingHandlerCallbackHead;
        while (task != null) {
            task.execute();
            task = task.next;
        }
    }

沿着调用链,最终执行了ChannelInitializer的handlerAdded

 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
 		// 此时进入这里
        if (ctx.channel().isRegistered()) {
            if (initChannel(ctx)) {
                removeState(ctx);
            }
        }
    }
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    	// 并发处理,避免线程执行过
        if (initMap.add(ctx)) { 
            try {
            	// 这里执行ChannelInitializer 匿名方法的操作
                initChannel((C) ctx.channel());
            } catch (Throwable cause) {
                ...
            } finally {
            	// 移除当前ChannelInitializer
                ChannelPipeline pipeline = ctx.pipeline();
                if (pipeline.context(this) != null) {
                    pipeline.remove(this);
                }
            }
            return true;
        }
        return false;
    }

这里为何要pipeline.remove(this)呢?
因为当前的上下文包裹的是一个ChannelInitializer ,ChannelInitializer是为解决ServerBootstrap不支持添加多个handler而诞生的,故而ChannelInitializer本身是没有用处的。

经过invokeHandlerAddedIfNeeded方法之后管道变成这样
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值