Netty启动的那点事(一)

背景

       对于netty一直比较好奇,之前看过《Netty权威指南》、《Netty实战》,但是可能因为自己对于netty没有很好的实践感受,才促使自己深入学习,也可能对于没有静下心来,多种原因吧。

       新年伊始,刚好年前一段时间对于netty也进去了项目上手,自己也能静下来一行一行阅读源码。

启动Netty服务端代码

public class HelloWorldServer {
    private int port;

    public HelloWorldServer(int port){
        this.port = port;
    }

    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try{
            //group指定ServerBootstrap的parentGroup 为bossGroup
            //childGroup为woker
            ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, worker)
            //channel调用的是ServerBootstrap的父类AbstractBootstrap,作用是初始化serverBootstrap的channelFactory,
            // 里面的参数表示以后创建出来的channel为NioServceSocketChannle对象,channel返回的是serverBootstrap对象
            .channel(NioServerSocketChannel.class)
            //给serverBootstrap初始化localAddress属性
            .localAddress(new InetSocketAddress(port))
            //初始化childHandler,返回serverBootstrap
            .childHandler(new ChannelInitializer() {
                        protected void initChannel(SocketChannel ch) throws Exception {
//                            ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                            ch.pipeline().addLast("decoder", new StringDecoder());
                            ch.pipeline().addLast("encoder", new StringEncoder());
                            ch.pipeline().addLast(new HelloWorldServerHandler());
                        };
                    })
            //给serverBootstrap中的option赋值,方法为AbstractBootstrap中的
            //option -->new LinkedHashMap, Object>();
            .option(ChannelOption.SO_BACKLOG, 128)
            //给serverBootstrap中childOption赋值,方法为ServerBootstrap中的
            //childOption -->new LinkedHashMap, Object>();
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 绑定端口,开始接收进来的连接
            //调用父类AbstractBootstrap中的bind
            //bind首先判断group和channelFactory是否为空,根据这个可知,这2个属性为必须的
            //最终调用AbstractBootstrap.doBind(localAddress); localAddress为.localAddress方法初始化的
            //doBind(final SocketAddress localAddress):
            // bind()返回的是DefaultChannelPromise对象
            //sync()只是让ChannelFuture挂起
            ChannelFuture future = serverBootstrap.bind(port).sync();

            System.out.println("Server start listen at " + port );
            future.channel().closeFuture().sync();

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new HelloWorldServer(port).start();
    }
}

这是一个启动Netty服务端的一代码,那么接下来进行一行行分析代码:

首先分析创建group:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();    

根据调用的栈关系,可以看到:

Netty启动的那点事(一)_第1张图片

最终进入了MultithreadEventExecutorGroup,

//nThreads,不指定的话,默认根据cpu核数的*2,类别线程池理解的话,这是核心线程数
//executor,调度线程工作的线程池,创建正在工作的线程
//chooserFactory,分配线程池的选择器
//args,可能会有2个,一个是maxPendingTasks,填充工作队列,一个是rejectedExecutionHandler,线程池的拒绝策略概念
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        //当有连接到监听端口时,工作线程
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                //如果是NioEventLoop,那么最终创建的是SingleThreadEventLoop
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                //创建失败了,异常时进入if里面
                if (!success) {
                    //创建失败,销毁已经创建成功的线程池
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }
        //选择分配线程的Executor
        //如果是2的幂的话,顺序选择根据 & 选择
        //如果是不是的话,则取余数
        //会有针对2的幂专门设计一个用 & ,这个细节可以分配线程的效率
        chooser = chooserFactory.newChooser(children);
        //为children设置监听Future
        final FutureListener terminationListener = new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }

        Set childrenSet = new LinkedHashSet(children.length);
        Collections.addAll(childrenSet, children);
        //这个没有明白是干啥的。。
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    } 
  

代码中嵌入的注释只是比较粗糙的解释了启动服务器调用这些方法的作用,接下来将详细的跟踪代码。

ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, worker)

进入ServerBootstrap发现有个group方法:

//可知group方法是重载方法
public ServerBootstrap group(EventLoopGroup group) {
        return group(group, group);
    }

    /**
     * Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
     * {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
     * {@link Channel}'s.
     */
    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;
    }

对于group的重载方式对应到三种线程模型,如果使用group(EventLoopGroup group)的话对应的是:

        单线程模型:该线程作为服务端(接受客户端TCP连接同时读取请求或者应答消息)、作为客户端(向服务端发起TCP连接同时向服务端发送请求或者应答消息);

.channel(NioServerSocketChannel.class)

debug进源码发现进入AbstractBootstrap

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

//跟踪进入AbstractBootstrap.BootstrapChannelFactory
 private static final class BootstrapChannelFactory implements ChannelFactory {
        private final Class clazz;

        BootstrapChannelFactory(Class clazz) {
            this.clazz = clazz;
        }
// 可知这是为创建Channel提供一个工厂类,通过反射创建Channel
        public T newChannel() {
            try {
                return (Channel)this.clazz.newInstance();
            } catch (Throwable var2) {
                throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
            }
        }

        public String toString() {
            return StringUtil.simpleClassName(this.clazz) + ".class";
        }
    }

通过BootstrapChannelFactory可知,Channel是通过反射形式来创建对象的,这也可以知道为什么很多Channel的实现类中都会带有默认的构造器,通过这个就能关联一起了。

localAddress()、childHandler()、option()跟childOption()都是为了ServerBootstrap赋予初值

比较重要的是bind()方法:

Netty启动的那点事(一)_第2张图片

根据调用栈帧分析,最终进入了AbstractBootstrap的doBind()中:

private ChannelFuture doBind(final SocketAddress localAddress) {
        //初始化ServerBootstrap相关配置,包括但不限于Channel的配置、创建创建Channel的工厂对象等
        //创建一个保存并执行任务的线程池,通过PowerOfTwoEventExecutorChooser来选择创建什么类型的线程池
        //此时的ChannelFuture 为DefaultChannelPromise.class
        //此时是为了返回与bind和connect相关的channel
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            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) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

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

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //通过在channel()中,指定了channelFactory为ReflectiveChannelFactory.class
            //此时的channel对象是通过反射创建而来的
            //此时的channel是为了bind和connect的时候知道
            channel = channelFactory.newChannel();
            //给NIO管道初始化
            //同时给ChannelPipeline增加默认的ChannelHandler
            //  ServerBootstrapAcceptor 和ServerBootstrapConfig.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();
                // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        //config return ServerBootstrapConfig
        //ServerBootstrapConfig.group() 返回的是 bossGroup EventLoopGroup
        // 此时的ServerBootstarp对象是否是创建ServerBootstrap的bossGroup
        // 疑问: 为啥不直接获取AbstractBootstarp中的group(),而要通过调用方法形式去获取group
        // 解答: 根据netty 4.x的class文件中,这行代码是this.group().register(channel);
            // 此时是没有走config()获取的
        //此时的ChannelFuture 为DefaultChannelPromise.class
        //group默认返回的是MultithreadEventLoopGroup.class
        //执行register的线程是MultithreadEventLoopExecutorGroup.class
        //真正执行register的是一个DefaultChannelPromise,真正注册的其实也是一个ChannelFuture,因为涉及到
        //I/O操作,所以都交付给异步线程去操作
        ChannelFuture regFuture = config().group().register(channel);
        //通过register中获取的线程池,并且往线程池中提交即将要执行IO任务,放入到线程池中同时返回保存任务的对象
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        // If we are here and the promise is not failed, it's one of the following cases:
        // 1) If we attempted registration from the event loop, the registration has been completed at this point.
        //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
        // 2) If we attempted registration from the other thread, the registration request has been successfully
        //    added to the event loop's task queue for later execution.
        //    i.e. It's safe to attempt bind() or connect() now:
        //         because bind() or connect() will be executed *after* the scheduled registration task is executed
        //         because register(), bind(), and connect() are all bound to the same thread.

        return regFuture;
    }

//ServerBootstrap类中的方法
void init(Channel channel) throws Exception {
        //里面的options再生成对象的时候创建,是一个linkHashMap
        //通过option()方法赋值
        final Map, Object> options = options0();
        //ServerBootstrap时的channel可以认为是NioServerSocketChannel.class 对象
        synchronized (options) {
            //给NIO通道初始化相关属性
            setChannelOptions(channel, options, logger);
        }
        // attrs = new LinkedHashMap, Object>();
        //如果未指定ServerBootstrap的attrs,此时attrs是为空的默认为空
        final Map, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey key = (AttributeKey) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
        //piple在创建channel的时候会创建,默认是DefaultChannelPipeline.class
        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry, Object>[] currentChildOptions;
        final Entry, Object>[] currentChildAttrs;
        //里面的options再生成对象的时候创建,是一个linkHashMap,初始里面不存放值
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        //新增默认的ChannelHandler,默认情况下为ServerBootstrapAcceptor
        p.addLast(new ChannelInitializer() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                //默认情况下hanler为空,所以ServerBootstrapAcceptor是第一个ChannelHandler
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                //ServerBootstrapAcceptor 本质也是ChannelHandler
                //所以第一个ChannelHandler是默认的
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }


private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
        // the pipeline in its channelRegistered() implementation.
        //此时的ChannelFuture 为DefaultChannelPromise.class
        //ServerBootstrap下channel为NioServerSocketChannel
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    //bind()作用是将ChannelHandler串成一个串,给ChannelFuture最终遍历去执行
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }
 
  

如有有误,欢迎指正并探讨。

你可能感兴趣的:(Netty)