Netty源码解析-ServerBootstrap及初始化服务端启动

2 篇文章 0 订阅
2 篇文章 0 订阅

       在https://blog.csdn.net/ybin__/article/details/81007018 中分析了NioEventGroupLoop的初始化,这一章主要ServerBootstrap的初始化,及Netty服务端的启动。

  1.  ServerBootstrap的UML图:

    ServerBootstrap继承至AbstractBootstrap,ServerBootStrap初始化的时候会将服务端的NioEventGroupLoop,handler及配置保存至AbstractBootStrap中,将客户端的配置保存至ServerBootStrap中。

服务端初始化代码:

public void run() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workGroup)                                   //1.保存NioEventLoopGroup
                .channel(NioServerSocketChannel.class)                  //2.设置服务端channel
                .option(ChannelOption.SO_BACKLOG, 128)                 //3.保存服务端配置
                .childOption(ChannelOption.SO_KEEPALIVE, true)         //4.保存客户端配置
                .handler(new ChannelHandler())                          //5.保存服务端handler
                .childHandler(new ChannelInitializer<SocketChannel>() { //6.保存客户端handler
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new ChannelInboundA(), new ChannelInboundB(), new ChannelInboundC());
                    }
                });

        try {
            ChannelFuture future = b.bind(port).sync();                 //7.启动服务端
            log.info("server start running");
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

 1).保存NioEventGroupLoop,如下,可以看到ServerBootstrap将处理客户端的childGroup保存至ServerBootStrap,将parentGroup保存至父类AbstractBootStrap中;

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }

2).保存服务端NioServerSocketChannel,在绑定端口的时候会反射调用它的构造方法,为服务端创建一个channel。
  

//1.调用无参构造
public NioServerSocketChannel() {
	this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
//2.获取初始化完成的SelectorProvider
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
//3.通过JDK的SelectorProvider打开一个ServerSocketChannel用于监听事件
private static ServerSocketChannel newSocket(SelectorProvider provider) {
	try {
		return provider.openServerSocketChannel();
	} catch (IOException e) {
		throw new ChannelException(
				"Failed to open a server socket.", e);
	}
}
//4.调用父类构造方法,并保存config;
public NioServerSocketChannel(ServerSocketChannel channel) {
	super(null, channel, SelectionKey.OP_ACCEPT);
	config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
//5.调用父类构造方法,并保存服务端需要监听的事件类型(SelectionKey.OP_ACCEPT),设置非阻塞
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
	super(parent);
	this.ch = ch;
	this.readInterestOp = readInterestOp;
	try {
		ch.configureBlocking(false);
	} catch (IOException e) {
		try {
			ch.close();
		} catch (IOException e2) {
			if (logger.isWarnEnabled()) {
				logger.warn(
						"Failed to close a partially initialized socket.", e2);
			}
		}

		throw new ChannelException("Failed to enter non-blocking mode.", e);
	}
}
//6.保存服务端channel,为该channel设置id,并实例化unsafe,后续获取内存等使用,并初始化pipeline,
//pipeline是netty的调用链路,本身的数据结构是一个链表,采用责任链模式处理事件的传播等;
protected AbstractChannel(Channel parent) {
	this.parent = parent;
	id = newId();
	unsafe = newUnsafe();
	pipeline = newChannelPipeline();
}

服务端启动:

  1. 在配置好ServerbootStrap后,调用bind(int prot)方法绑定监听端口,监听事件;
  2. 最后调用到io.netty.bootstrap.AbstractBootstrap#doBind方法,在该方法中,netty会初始化并注册channel到selector的多路复用器上,及上述NioServerSocketChannel初始化的过程。

    channel初始化完成后,调用io.netty.bootstrap.ServerBootstrap#init方法,为channel设置上创建ServerBootStrap时配置的参数,如IO参数,channelHandler事件处理器等。   

    最后调用获取配置的boosGroup,并调用其register(channel)方法,即io.netty.channel.MultithreadEventLoopGroup#register。在该方法中,会先调用io.netty.channel.MultithreadEventLoopGroup#next获取可能空闲的NioEventLoop,这里实现方式实际就是上一章说到的netty使用策略模式为NioEventLoop提供了两种选择策略,如果NioEventLoopGroup的线程数是2的幂次方则是每次取自增前的数 & 线程池长度-1,如果不是就是自增前的数 对 线程池长度取余。

    获取到可能空闲的NioEventLoop后最终调用到io.netty.channel.nio.AbstractNioChannel#doRegister,将NioEventLoop的selector注册到javaChannel上,并返回需要关注的事件。
    final ChannelFuture initAndRegister() {
    	Channel channel = null;
    	try {
    		channel = channelFactory.newChannel();
    		init(channel);
    	} catch (Throwable t) {
    		if (channel != null) {
    			channel.unsafe().closeForcibly();
    		}
    		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;
    }
    @Override
        protected void doRegister() throws Exception {
            boolean selected = false;
            for (;;) {
                try {
                    selectionKey = javaChannel().register(eventLoop().selector, 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;
                    }
                }
            }
        }

     

  3. 最后调用到io.netty.bootstrap.AbstractBootstrap#doBind0,获取该channel的NioEventLoop,提交一个绑定端口的任务,执行io.netty.util.concurrent.SingleThreadEventExecutor#execute中的execute方法;

     
    private static void doBind0(
                final ChannelFuture regFuture, final Channel channel,
                final SocketAddress localAddress, final ChannelPromise promise) {
    	channel.eventLoop().execute(new Runnable() {
    		@Override
    		public void run() {
    			if (regFuture.isSuccess()) {
    				channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    			} else {
    				promise.setFailure(regFuture.cause());
    			}
    		}
    	});
    }

     

  4. 在io.netty.util.concurrent.SingleThreadEventExecutor#execute中会首先判断当前线程是否是选择的NioEvenLoop中保存执行线程,不是的话则会调用io.netty.util.concurrent.SingleThreadEventExecutor#startThread方法,如果该线程是第一次start则会启动事件轮询处理线程(下章分析),并将绑定端口的任务加到NioEventLoop的任务执行队列中,交由事件轮询处理任务处理,这里就是Nettyr异步串行处理任务的体现,将不是同一个NioEventLoop线程的任务新起一个线程执行,而同线程的任务,则放到该NioEventLoop的队列中,等NioEventLoop的线程来处理,避免了多线的竞争,不同线程的任务启动新线程异步执行。


    到这里netty服务端已启动,下面就开始监听事件,处理事件了,下一章再继续分析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值