美文网首页
EchoServer启动(1)

EchoServer启动(1)

作者: 追梦小蜗牛 | 来源:发表于2020-06-14 16:35 被阅读0次
    image.png

    简介

    Echoes back any received data from a client,本编文章会分析一下server端代码的启动流程,梳理一下自己的理解。

    代码分析

        public static void main(String[] args) throws Exception {
            // Configure the server.
            ServerBootstrap bootstrap = new ServerBootstrap(@1
                    new NioServerSocketChannelFactory(
                            Executors.newCachedThreadPool(),
                            Executors.newCachedThreadPool()));
    
            // Set up the default event pipeline.
            EchoHandler handler = new EchoHandler();@2
            bootstrap.getPipeline().addLast("handler", handler);@3
    
            // Bind and start to accept incoming connections.
            bootstrap.bind(new InetSocketAddress(8080));@4
    
            // Start performance monitor.
            new ThroughputMonitor(handler).start();
        }
    

    @1:处其实就是构建一个BootStrap对象,同时在构造方法里面初始化一些必要的参数,发现很多开元框架都喜欢在构造方法里面去做一些初始化操作。分别指定用来执行boss threads和I/O worker threads的Executor。
    @2:构建一个Handler对象,可以是自定义的,用来处理具体的业务。
    @3:构建一个DefaultChannelPipeline对象,并把涉及到的Handler加入到上面。
    @4:绑定端口,开始监听客户端发过来的请求。

    代码细节

    @1:我们可以进入到NioServerSocketChannelFactory构造函数里面:

    public NioServerSocketChannelFactory(
                Executor bossExecutor, Executor workerExecutor,
                int workerCount) {
            if (bossExecutor == null) {
                throw new NullPointerException("bossExecutor");
            }
            if (workerExecutor == null) {
                throw new NullPointerException("workerExecutor");
            }
            if (workerCount <= 0) {
                throw new IllegalArgumentException(
                        "workerCount (" + workerCount + ") " +
                        "must be a positive integer.");
            }
            this.bossExecutor = bossExecutor;@5
            this.workerExecutor = workerExecutor;@6
            sink = new NioServerSocketPipelineSink(workerExecutor, workerCount);@7
        }
    
        NioServerSocketPipelineSink(Executor workerExecutor, int workerCount) {@8
            workers = new NioWorker[workerCount];
            for (int i = 0; i < workers.length; i ++) 
                workers[i] = new NioWorker(id, i + 1, workerExecutor);
            }
        }
    

    @5和@6 分别是把CachedThreadPool赋值给final类型的bossExecutor 和workerExecutor ,一般发现源码里面有很多对象都会定义成final的,其实这有利于程序的编译,明确指出一些不会发生变化的变量和对象,同时可以告诉别人这个对象是线程安全的,不可以修改等等。
    @7:进入到NioServerSocketPipelineSink代码里面看下,这个方法主要做的工作是:初始化NioWorker数组并赋值。@8处:默认工作线程数组的大小是当前点电脑的cpu核数。你也可以自定义线程池,自定义各个参数。

    我们可也进入到@4的代码:如下核心方法bind,它主要是用来创建一个和指定地址绑定的channel。

    public Channel bind(final SocketAddress localAddress) {
            if (localAddress == null) {
                throw new NullPointerException("localAddress");
            }
    
            final BlockingQueue<ChannelFuture> futureQueue =
                new LinkedBlockingQueue<ChannelFuture>();
    
            ChannelPipeline bossPipeline = pipeline();
            bossPipeline.addLast("binder", new Binder(localAddress, futureQueue));
    
            ChannelHandler parentHandler = getParentHandler();
            if (parentHandler != null) {
                bossPipeline.addLast("userHandler", parentHandler);
            }
    
            Channel channel = getFactory().newChannel(bossPipeline);@9
    
            // Wait until the future is available.
            ChannelFuture future = null;
            do {
                try {
                    future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    // Ignore
                }
            } while (future == null);
    
            // Wait for the future.
            future.awaitUninterruptibly();
            if (!future.isSuccess()) {
                future.getChannel().close().awaitUninterruptibly();
                throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
            }
    
            return channel;
        }
    

    重点来看下@9处的代码:主要是create和open一个channel,并且附加上指定的ChannelPipeline。它是ChannelFactory的方法,进入到对应的实现类:NioServerSocketChannelFactory:

        public ServerSocketChannel newChannel(ChannelPipeline pipeline) {
            return new NioServerSocketChannel(this, pipeline, sink);@10
        }
    

    sink参数是在上一步初始化过的,进入到NioServerSocketChannel构造方法里面:

    NioServerSocketChannel(
                ChannelFactory factory,
                ChannelPipeline pipeline,
                ChannelSink sink) {
    
            super(factory, pipeline, sink);
    
            try {
                socket = ServerSocketChannel.open();@11
            } catch (IOException e) {
                throw new ChannelException(
                        "Failed to open a server socket.", e);
            }
    
            try {
                socket.configureBlocking(false);
            } catch (IOException e) {
                try {
                    socket.close();
                } catch (IOException e2) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
    
                throw new ChannelException("Failed to enter non-blocking mode.", e);
            }
    
            config = new DefaultServerSocketChannelConfig(socket.socket());
    
            fireChannelOpen(this);@12
        }
    

    感觉这个方法才是重点,点击进去@11处的代码:

    public static SelectorProvider provider() {
            synchronized (lock) {
                if (provider != null)
                    return provider;
                return AccessController.doPrivileged(
                    new PrivilegedAction<SelectorProvider>() {
                        public SelectorProvider run() {
                                if (loadProviderFromProperty())
                                    return provider;
                                if (loadProviderAsService())
                                    return provider;
                                provider = sun.nio.ch.DefaultSelectorProvider.create();
                                return provider;
                            }
                        });
            }
        }
    

    provider方法返回的是此Java虚拟机调用的系统范围的默认SelectorProvider ,这一点和操作系统有点关系。
    然后进入到@12处的代码如下:

        public static void fireChannelOpen(Channel channel) {
            // Notify the parent handler.
            if (channel.getParent() != null) {
                fireChildChannelStateChanged(channel.getParent(), channel);
            }
    
            channel.getPipeline().sendUpstream(
                    new UpstreamChannelStateEvent(
                            channel, ChannelState.OPEN, Boolean.TRUE));
        }
    

    这个方法主要是发送一个channelOpen event到当前channel关联的pipeline的handler上面,然后进行对应的业务处理,这个启动服务类上面绑定了两个handler,一个Binder,一个EchoHandler。重点是pipeline的sendUpstream方法,里面引出来的东西有很多,放到下一个文章里面分析。

    总结

    工匠精神......我来了......不要老羡慕别人是大佬,其实你自己很快也将成为大佬.....

    相关文章

      网友评论

          本文标题:EchoServer启动(1)

          本文链接:https://www.haomeiwen.com/subject/fobxxktx.html