Netty中ServerBootstrap初始化源码解析

前言

之前的文章介绍了Netty的线程池NioEventLoopGroup的初始化过程,这次将分析Netty中同样非常重要的一个东西ServerBootstrap。

ServerBootstrap使用片段

        ThreadFactory bossThreadFactory = new ThreadFactoryBuilder().setNameFormat(getServerName() + " Server Acceptor NIO Thread#%d").build();
        ThreadFactory workerThreadFactory = new ThreadFactoryBuilder().setNameFormat(getServerName() + " Server Reactor NIO Thread#%d").build();

        this.bossGroup = new NioEventLoopGroup(numberOfThreads, bossThreadFactory);
        this.workerGroup = new NioEventLoopGroup(numberOfThreads, workerThreadFactory);

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast("readTimeoutHandler", new ReadTimeoutHandler(DeviceServerListener.this.timeoutSeconds));
                pipeline.addLast("lineBasedFrameDecoder-" + maxLength, new LineBasedFrameDecoder(Integer.parseInt(maxLength)));// 按行('\n')解析成命令ByteBuf
                pipeline.addLast("stringPluginMessageDecoder", new StringDecoder(CharsetUtil.UTF_8));
                pipeline.addLast("stringToByteEncoder", new StringToByteEncoder());// 将JSON字符串类型消息转换成ByteBuf
                pipeline.addLast("deviceMessageDecoder", new DeviceMessageDecoder());// 将JSON字符串消息转成deviceMessage对象
                pipeline.addLast("deviceMessageEncoder", new DeviceMessageEncoder());// 将deviceMessage对象转成JSON字符串
                pipeline.addLast("deviceHeartBeatResponseHandler", new DeviceHeartBeatResponseHandler(heartTime));
                pipeline.addLast("deviceAuthResponseHandler",
                        new DeviceAuthResponseHandler(DeviceServerListener.this.timeoutSeconds, DeviceServerListener.serverInstanceName));
                pipeline.addLast("deviceMessageHandler", new DeviceMessageHandler());

                // log.debug("Added Handler to Pipeline: {}", pipeline.names());
            }
        }).option(ChannelOption.SO_BACKLOG, 1024).option(ChannelOption.SO_KEEPALIVE, true).option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);

        // Start the server. Bind and start to accept incoming connections.
        this.channelFuture = bootstrap.bind(serverPort).sync();

这是Netty服务端比较标准的初始化片段,可以看到这其中ServerBootstrap有着非常重要的戏份,它就像是Netty的启动器一样,其中的NioEventLoopGroup之前已经分析过了,那么直接来看ServerBootstrap的初始化。

源码解析

从以上片段可以看到初始化时首先通过ServerBootstrap的无参构造函数创建一个对象,该构造函数没有任何的操作因此不做分析。接着是该对象的一串链式调用bootstrap.group().channel().childHandler().option(),我们来逐一看一下。
首先是group()方法

    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;
    }

这里将传入的childGroup对象赋值给ServerBootstrapchildGroup属性,然后调用了父类AbstractBootstrapgroup()方法

    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return (B) this;
    }

由这里看出该方法将传入的parentGroup赋值给了group属性。

接着来看channel()方法

    public B channel(Class channelClass) {
        if (channelClass == null) {
            throw new NullPointerException("channelClass");
        }
        return channelFactory(new BootstrapChannelFactory(channelClass));
    }

这里调用了channelFactory(new BootstrapChannelFactory(channelClass)),来跟一下

    public B channelFactory(ChannelFactory channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return (B) this;
    }

这里完成了对AbstractBootstrap类的channelFactory属性的赋值,赋值对象为new BootstrapChannelFactory(channelClass)

public abstract class AbstractBootstrap, C extends Channel> implements Cloneable

这里的C根据AbstractBootstrap的类定义为Channel的子类,因此实际传入的channelClass值为NioServerSocketChannel.class

接着来看childHandler()方法

    public ServerBootstrap childHandler(ChannelHandler childHandler) {
        if (childHandler == null) {
            throw new NullPointerException("childHandler");
        }
        this.childHandler = childHandler;
        return this;
    }

没有过多操作,也仅是对ServerBootstrapchildHandler属性赋值,但这里传入的childHandler稍微有点复杂。传入的对象为new ChannelInitializer()同时覆写了其initChannel(SocketChannel ch)方法,该方法初始化了一个处理链用于处理接收和发送双向的消息,这块比较重要,后续将会单独进行分析,在此暂不深入。

最后来看一下option()方法

    public  B option(ChannelOption 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 (B) this;
    }

这里维护了一个options数组将要设置的项都放进了该数组中,其定义如下

private final Map, Object> options = new LinkedHashMap, Object>();

这里可以看出放入对象的key值限定为ChannelOption对象,大致都有下面这些

    public static final ChannelOption ALLOCATOR = valueOf("ALLOCATOR");//根据PooledByteBufAllocator或UnpooledByteBufAllocator设置对象池开启与否(4.1版本开始默认开)
    public static final ChannelOption RCVBUF_ALLOCATOR = valueOf("RCVBUF_ALLOCATOR");//设置接收池
    public static final ChannelOption MESSAGE_SIZE_ESTIMATOR = valueOf("MESSAGE_SIZE_ESTIMATOR");

    public static final ChannelOption CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");
    public static final ChannelOption MAX_MESSAGES_PER_READ = valueOf("MAX_MESSAGES_PER_READ");
    public static final ChannelOption WRITE_SPIN_COUNT = valueOf("WRITE_SPIN_COUNT");
    public static final ChannelOption WRITE_BUFFER_HIGH_WATER_MARK = valueOf("WRITE_BUFFER_HIGH_WATER_MARK");
    public static final ChannelOption WRITE_BUFFER_LOW_WATER_MARK = valueOf("WRITE_BUFFER_LOW_WATER_MARK");

    public static final ChannelOption ALLOW_HALF_CLOSURE = valueOf("ALLOW_HALF_CLOSURE");
    public static final ChannelOption AUTO_READ = valueOf("AUTO_READ");

    public static final ChannelOption SO_BROADCAST = valueOf("SO_BROADCAST");//允许发送广播数据报
    public static final ChannelOption SO_KEEPALIVE = valueOf("SO_KEEPALIVE");//是否启用心跳保活机制
    public static final ChannelOption SO_SNDBUF = valueOf("SO_SNDBUF");//发送缓冲区大小
    public static final ChannelOption SO_RCVBUF = valueOf("SO_RCVBUF");//接收缓冲区大小
    public static final ChannelOption SO_REUSEADDR = valueOf("SO_REUSEADDR");//是否允许重复使用本地地址和端口
    public static final ChannelOption SO_LINGER = valueOf("SO_LINGER");//保证阻塞close()的调用,直到数据完全发送
    public static final ChannelOption SO_BACKLOG = valueOf("SO_BACKLOG");//临时存放已完成三次握手的请求队列的最大长度

    public static final ChannelOption SO_TIMEOUT = valueOf("SO_TIMEOUT");

    public static final ChannelOption IP_TOS = valueOf("IP_TOS");
    public static final ChannelOption IP_MULTICAST_ADDR = valueOf("IP_MULTICAST_ADDR");
    public static final ChannelOption IP_MULTICAST_IF = valueOf("IP_MULTICAST_IF");
    public static final ChannelOption IP_MULTICAST_TTL = valueOf("IP_MULTICAST_TTL");
    public static final ChannelOption IP_MULTICAST_LOOP_DISABLED = valueOf("IP_MULTICAST_LOOP_DISABLED");

    public static final ChannelOption TCP_NODELAY = valueOf("TCP_NODELAY");//是否开启Nagle算法,即是否等数据累积到一定程度再发送数据

这里很大一部分是tcp中的一些参数设置,比较常用的几个参数都在源码上做了注释,其中对性能影响很大的一项是ALLOCATOR,根据一些资料显示开启对象池性能远高于不开启,而Netty从4.1版本开始也将默认选项设为了开启对象池。
这里我们顺便来看一下valueOf方法的实现

    public static  ChannelOption valueOf(String name) {
        checkNotNull(name, "name");
        ChannelOption option = names.get(name);
        if (option == null) {
            option = new ChannelOption(name);
            ChannelOption old = names.putIfAbsent(name, option);
            if (old != null) {
                option = old;
            }
        }
        return option;
    }

可以看到这个方法主要是维护了names这个数组,其定义如下

private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap();

来看一下PlatformDependent.newConcurrentHashMap()的具体实现

    public static  ConcurrentMap newConcurrentHashMap() {
        if (CAN_USE_CHM_V8) {
            return new ConcurrentHashMapV8();
        } else {
            return new ConcurrentHashMap();
        }
    }

看到这里我又一次被震惊了,Netty自己实现了一个ConcurrentHashMapV8用于应对java8以下的版本,要知道这可是一个6000多行代码的类。。。不得不说Netty的开发者真的不愧优化狂魔的称号。

分析完了bootstrap.group().channel().childHandler().option()这一串链式调用,接着来看下一串bootstrap.bind(serverPort).sync()。这里首先来看bind()方法

    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

这里根据端口号创建了一个InetSocketAddress对象,并调用了bind(SocketAddress localAddress)方法

    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }

这里的validate()方法校验了AbstractBootstrap类的groupchannelFactory属性是否为空,然后执行doBind()方法

    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();// 创建Channel并注册到线程池
        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 {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // 注册过程中有异常则失败
                        promise.setFailure(cause);
                    } else {
                        // 注册完成且成功
                        promise.executor = channel.eventLoop();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

首先看一下regFuture对象如何通过initAndRegister()方法生成的

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
             // 创建一个Channel
            channel = channelFactory().newChannel();
            // 初始化处理器Handler
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
            }
            // Channel还没有注册到线程池,使用默认线程GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        // 将channel注册到Reactor线程池
        ChannelFuture regFuture = group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

channelFactory().newChannel()这里调用的是AbstractBootstrapchannelFactory属性的newChannel()方法,根据之前赋值的channelFactory对象的情况来看,这里最终得到的是NioServerSocketChannel的一个实例对象。得到channel对象后,接着调用init(channel)进行了初始化,该方法由子类ServerBootstrap实现。

    @Override
    void init(Channel channel) throws Exception {
        final Map, Object> options = options();
        synchronized (options) {
            channel.config().setOptions(options);//将options中逐一设置到DefaultChannelConfig对象上
        }

        final Map, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey key = (AttributeKey) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry, Object>[] currentChildOptions;
        final Entry, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }

        p.addLast(new ChannelInitializer() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {//为了把ServerBootstrapAcceptor放在处理链的最末端,该类主要功能是将mainReactor接受的Channel传递给subReactor
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }
 
 

这个方法看似很长其实主要做了两件事:一、将父类和子类的options和attrs进行赋值;二、构建channelpipeline属性的处理链。这里主要来看一下addLast()方法,由DefaultChannelPipeline进行具体实现,来看一下

    @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) {
            checkMultiplicity(handler);//判断handler是否已添加并且是否是sharable的

            newCtx = newContext(group, filterName(name, handler), handler);//filterName(name, handler)自动生成name,newContext方法对newCtx对象的handler、inbound、outbound、name、pipeline属性进行了赋值

            addLast0(newCtx);//将newCtx加到tail节点之前

            if (!registered) {//一旦channel注册到eventloop上就将registered置为true,并且不可再改变
                newCtx.setAddPending();//CAS地修改newCtx的handlerState的值
                callHandlerCallbackLater(newCtx, true);//添加newCtx到pendingHandlerCallbackHead的next节点
                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;
    }

最终执行的是三参的addLast()方法并且传入的groupname均为空。该方法中主要根据channel是否注册到eventloop上做不同的处理,初始化handler链。
回到initAndRegister()方法中,分析完了init(channel)再来看另一个很重要的将channel注册到Reactor线程池的register()方法。该方法在NioEventLoopGroup的父类MultithreadEventLoopGroup中实现

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

    @Override
    public EventLoop next() {
        return (EventLoop) super.next();
    }

    @Override
    public EventExecutor next() {
        return chooser.next();
    }

这里的chooser上一篇文章中有分析过,其根据线程池设置的线程数是否2的幂次方有不同的实现,而next()方法返回的是数组中的一个NioEventLoop对象,该用类的register()方法,最终会返回一个注册了该channelDefaultChannelPromise对象,具体这里就不再深入了。
到此initAndRegister()方法就分析完毕,回到doBind()方法来继续分析doBind0()方法

    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());
                }
            }
        });
    }

这里能看到,只有regFuture.isSuccess()也就是channel注册成功时时才会执行绑定操作channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE),否则直接向promise写注册失败,这里的promise是一个DefaultChannelPromise类型的对象,该类继承自Future,可以认为是一种特殊的Future对象。bind(localAddress, promise)方法最终会绑定在tail节点上,最终交由DefaultChannelPipeline的内部类的unsafe去进行绑定,调用链非常深在此就不做展开了。
最后跳回来看一下bootstrap.bind(serverPort).sync()中的sync(),之前分析过bootstrap.bind(serverPort)返回的是promise,因此sync()方法由DefaultChannelPromise实现

    @Override
    public ChannelPromise sync() throws InterruptedException {
        super.sync();
        return this;
    }

接着跟到DefaultPromise

    @Override
    public Promise sync() throws InterruptedException {
        await();
        rethrowIfFailed();
        return this;
    }

    @Override
    public Promise await() throws InterruptedException {
        if (isDone()) {
            return this;
        }

        if (Thread.interrupted()) {
            throw new InterruptedException(toString());
        }

        checkDeadLock();

        synchronized (this) {
            while (!isDone()) {
                incWaiters();
                try {
                    wait();
                } finally {
                    decWaiters();
                }
            }
        }
        return this;
    }

可以看出正常情况下该方法会始终在while循环中,导致执行到sync()方法的线程阻塞,因此bootstrap.bind(serverPort).sync()以后的代码都是不可达的,这点值得注意。

总结

ServerBootstrap的使用方式非常固定,大部分常规使用都会应用该初始化代码模板。主要做的事情就是赋值ServerBootstrap的各个属性,并且创建Channel、绑定用户定义的Handler、以及将该Channel注册到一个eventloop中(这里特别要强调的是Channel只能被绑定到一个eventloop中),最后绑定本地端口监听IO事件。

你可能感兴趣的:(Netty中ServerBootstrap初始化源码解析)