MINA框架源码分析(二)

        上一篇我们通过实例学习了MINA框架的用法,发现用起来还是挺方便的,就那么几步就可以了,本着学东西必知其原理的观念,决定看看MINA的源码实现,好了,我们开始吧!

        MINA源码对于客户端和服务端来说基本上差别不是很大的,所以我计划主要还是分析服务端的源码,在正式分析之前,我们需要对MINA有一个整体的了解;

        MINA中涉及到了这么几个对象:

        IoService:用于提供连接,他是IoAcceptor和IoConnector的父接口;

        IoBuffer:消息缓存区;

        IoSession:在每一次连接建立成功之后都会创建一个IoSession对象出来,并且在创建该对象的时候创建一个IoFilter对象出来,通过IoSession的session id来为当前IoSession设置处理他的IoProcessor;

        IoProcessor:用于检查是否有数据在通道上面进行读写,在我们创建Acceptor或者Connector的时候,默认会创建一个线程池,里面存储的就是IoProcessor线程,该线程里面是拥有自己的Selector的,这个是MINA为我们做的一点优化,我们通常使用NIO的话是只有一个Selector的,而MINA中的

        IoFilter:用于定义拦截器,这些拦截器可以包括日志输出、数据编解码等等,只要用于二进制数据和对象之间的转换;

        IoHandler:处于IoFilter的尾部,用于真正的业务逻辑处理,所以我们在使用MINA的时候是必须要提供IoHandler对象的,因为是靠他来进行真正业务处理的;

        接下来我们看看上篇博客中我们用到的MINA中涉及到的这几个对象的类结构图:

        NioSocketAcceptor类结构图:

                   

        NioSocketConnector类结构图:

                  

        NioSocketSession类结构图:

                                                            

        NioProcessor类结构图:

                                             

        好了,开始我们真正的源码分析了(服务端);

        首先我们通过NioSocketAcceptor acceptor = new NioSocketAcceptor();创建了一个NioSocketAcceptor对象出来,那我们就得看看NioSocketAcceptor的构造函数里面做了些什么事了;

       NioSocketAcceptor$NioSocketAcceptor()

public NioSocketAcceptor() {
        super(new DefaultSocketSessionConfig(), NioProcessor.class);
        ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
    }
        可以看到首先调用了父类的构造函数,也就是AbstractPollingIoAcceptor的构造函数,并且传入了NioProcessor的Class对象,这里我们可以想象一下后面肯定会用这个NioProcessor的Class对象进行一些与反射有关的操作;

        AbstractPollingIoAcceptor$AbstractPollingIoAcceptor()

protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {
        this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass), true, null);
    }
        可以看到实际上调用的是5个参数的构造函数,在看这个构造函数之前,我们看到第三个参数利用我们从NioSocketAcceptor构造函数中传进来的NioProcessor对象,创建了一个SimpleIoProcessorPool对象,我们来看看SimpleIoProcessorPool的构造函数;
        SimpleIoProcessorPool$SimpleIoProcessorPool()  

   public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType) {
        this(processorType, null, DEFAULT_SIZE, null);
    }
        发现他接着调用的是SimpleIoProcessorPool四个参数的构造函数,并且添加了一个DEFAULT_SIZE参数,这个值的大小等于我们CPU的核数+1,这也是我们在创建NioSocketAcceptor的时候默认创建的NioProcessor的线程个数,来看看SimpleIoProcessorPool四个参数的构造函数:

public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, Executor executor, int size, 
            SelectorProvider selectorProvider) {
        if (processorType == null) {
            throw new IllegalArgumentException("processorType");
        }

        if (size <= 0) {
            throw new IllegalArgumentException("size: " + size + " (expected: positive integer)");
        }

        // Create the executor if none is provided
        createdExecutor = (executor == null);

        if (createdExecutor) {
            this.executor = Executors.newCachedThreadPool();
            // Set a default reject handler
            ((ThreadPoolExecutor) this.executor).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        } else {
            this.executor = executor;
        }

        pool = new IoProcessor[size];

        boolean success = false;
        Constructor<? extends IoProcessor<S>> processorConstructor = null;
        boolean usesExecutorArg = true;

        try {
            // We create at least one processor
            try {
                try {
                    processorConstructor = processorType.getConstructor(ExecutorService.class);
                    pool[0] = processorConstructor.newInstance(this.executor);
                } catch (NoSuchMethodException e1) {
                    // To the next step...
                    try {
                        if(selectorProvider==null) {
                            processorConstructor = processorType.getConstructor(Executor.class);
                            pool[0] = processorConstructor.newInstance(this.executor);
                        } else {
                            processorConstructor = processorType.getConstructor(Executor.class, SelectorProvider.class);
                            pool[0] = processorConstructor.newInstance(this.executor,selectorProvider);
                        }
                    } catch (NoSuchMethodException e2) {
                        // To the next step...
                        try {
                            processorConstructor = processorType.getConstructor();
                            usesExecutorArg = false;
                            pool[0] = processorConstructor.newInstance();
                        } catch (NoSuchMethodException e3) {
                            // To the next step...
                        }
                    }
                }
            } catch (RuntimeException re) {
                LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage());
                throw re;
            } catch (Exception e) {
                String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage();
                LOGGER.error(msg, e);
                throw new RuntimeIoException(msg, e);
            }

            if (processorConstructor == null) {
                // Raise an exception if no proper constructor is found.
                String msg = String.valueOf(processorType) + " must have a public constructor with one "
                        + ExecutorService.class.getSimpleName() + " parameter, a public constructor with one "
                        + Executor.class.getSimpleName() + " parameter or a public default constructor.";
                LOGGER.error(msg);
                throw new IllegalArgumentException(msg);
            }

            // Constructor found now use it for all subsequent instantiations
            for (int i = 1; i < pool.length; i++) {
                try {
                    if (usesExecutorArg) {
                        if(selectorProvider==null) {
                            pool[i] = processorConstructor.newInstance(this.executor);
                        } else {
                            pool[i] = processorConstructor.newInstance(this.executor, selectorProvider);
                        }
                    } else {
                        pool[i] = processorConstructor.newInstance();
                    }
                } catch (Exception e) {
                    // Won't happen because it has been done previously
                }
            }

            success = true;
        } finally {
            if (!success) {
                dispose();
            }
        }
    }

        这段代码相对来说比较长,可以看到在第14行判断传入SimpleIoProcessorPool的executor是否为null,为null的话执行第15行,创建一个CachedThreadPool类型的线程池,随后在第32行通过反射获取到processorType参数为ExecutorService的构造函数,我们这里的processType实际上就是NioProcessor,随后33行通过反射创建一个NioProcessor对象出来,调用的是他的下面这个构造函数:

 public NioProcessor(Executor executor) {
        super(executor);

        try {
            // Open a new selector
            selector = Selector.open();
        } catch (IOException e) {
            throw new RuntimeIoException("Failed to open a selector.", e);
        }
    }
 可以注意到的是在SimpleIoProcessorPool里面有两种通过反射创建NioProcessor对象的方式,就是我们上面代码的第78和80这两种方式,两者的区别在于如果我们在创建SimpleIoProcessorPool的时候传入了SelectorProvider对象,那么NioProcessor里面的Selector将直接调用SelectorProvider的openSelector来获得,而如果没有传入SelectorProvider对象的话,NioProcessor里面的Selector将通过Selector.open方法获得;

        到此,我们创建出来了CPU个数+1个NioProcessor,每个NioProcessor里面都会有一个Selector对象;
        我们回到AbstractPollingIoAcceptor的构造函数

private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,
            boolean createdProcessor, SelectorProvider selectorProvider) {
        super(sessionConfig, executor);

        if (processor == null) {
            throw new IllegalArgumentException("processor");
        }

        this.processor = processor;
        this.createdProcessor = createdProcessor;

        try {
            // Initialize the selector
            init(selectorProvider);

            // The selector is now ready, we can switch the
            // flag to true so that incoming connection can be accepted
            selectable = true;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeIoException("Failed to initialize.", e);
        } finally {
            if (!selectable) {
                try {
                    destroy();
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);
                }
            }
        }
    }
        首先执行了super构造函数,这个构造函数实际上执行的是AbstractIoService的构造函数;

protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor) {
        if (sessionConfig == null) {
            throw new IllegalArgumentException("sessionConfig");
        }

        if (getTransportMetadata() == null) {
            throw new IllegalArgumentException("TransportMetadata");
        }

        if (!getTransportMetadata().getSessionConfigType().isAssignableFrom(sessionConfig.getClass())) {
            throw new IllegalArgumentException("sessionConfig type: " + sessionConfig.getClass() + " (expected: "
                    + getTransportMetadata().getSessionConfigType() + ")");
        }

        // Create the listeners, and add a first listener : a activation listener
        // for this service, which will give information on the service state.
        listeners = new IoServiceListenerSupport(this);
        listeners.add(serviceActivationListener);

        // Stores the given session configuration
        this.sessionConfig = sessionConfig;

        // Make JVM load the exception monitor before some transports
        // change the thread context class loader.
        ExceptionMonitor.getInstance();

        if (executor == null) {
            this.executor = Executors.newCachedThreadPool();
            createdExecutor = true;
        } else {
            this.executor = executor;
            createdExecutor = false;
        }

        threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
    }

        这个构造函数会判断我们的executor是否为null,为null的话会创建一个CachedThreadPool出来,这里我们传入给AbstractPollingIoAcceptor的参数值为null,因此会创建一个Executor出来;

        可以看到第14行执行了init方法,传入的参数是SelectorProvider类型对象,这个方法在AbstractPollingIoAcceptor里面并没有实现,因此查看AbstractPollingIoAcceptor的子类NioSocketAcceptor的init(SelectorProvider)方法

protected void init(SelectorProvider selectorProvider) throws Exception {
        this.selectorProvider = selectorProvider;

        if (selectorProvider == null) {
            selector = Selector.open();
        } else {
            selector = selectorProvider.openSelector();
        }
    }

        这个方法所做的事还是比较简单的,就是创建根据SelectorProvider是否为空创建Selector对象而已,注意这个Selector对象是属于NioSocketAcceptor的;

        在init执行结束之后,AbstractPollingIoAcceptor构造函数第18行会将selectable设置为true,表示我们NioSocketAcceptor里面的Selector对象已经创建结束了,我们可以处理随后客户端到来的连接请求了;

        至此,NioSocketAcceptor的构造方法执行结束了,在这个构造方法中为我们创建出了CPU个数+1个NioProcess对象,每个对象里面都包含一个Selector对象,同时也为NioSocketAcceptor创建了一个Selector对象,同时从上面可以发现我们的NioSocketAcceptor和SimpleIoProcessorPool里的线程池可以是同一个也可以不是同一个,具体就在你创建NioSocketAcceptor和SimpleIoProcessorPool是否传入同一个Executor就可以啦; 
        有了NioSocketAcceptor对象之后,我们通过有了NioSocketAcceptor的bind方法将他和某一个端口绑定起来,因此查看NioSocketAcceptor的bind方法,你会发现根本不存在,那么根据前面NioSocketAcceptor的类结构图,去他的父类AbstractPollingIoAcceptor查找,还是没有,那只能继续向上找,找到AbstractIoAcceptor里面,终于找到了;

        AbstractIoAcceptor$bind()

public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
        if (isDisposing()) {
            throw new IllegalStateException("The Accpetor disposed is being disposed.");
        }

        if (localAddresses == null) {
            throw new IllegalArgumentException("localAddresses");
        }

        List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();

        for (SocketAddress a : localAddresses) {
            checkAddressType(a);
            localAddressesCopy.add(a);
        }

        if (localAddressesCopy.isEmpty()) {
            throw new IllegalArgumentException("localAddresses is empty.");
        }

        boolean activate = false;
        synchronized (bindLock) {
            synchronized (boundAddresses) {
                if (boundAddresses.isEmpty()) {
                    activate = true;
                }
            }

            if (getHandler() == null) {
                throw new IllegalStateException("handler is not set.");
            }

            try {
                Set<SocketAddress> addresses = bindInternal(localAddressesCopy);

                synchronized (boundAddresses) {
                    boundAddresses.addAll(addresses);
                }
            } catch (IOException e) {
                throw e;
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e);
            }
        }

        if (activate) {
            getListeners().fireServiceActivated();
        }
    }
        不管你调用的是哪个bind方法,最后执行的都是这个bind方法,在这个方法中首先会进行迭代,将所有需要绑定的地址存储到localAddressesCopy里面,随后在第34行调用bindInternal方法进行绑定,这个方法在AbstractIoAcceptor里面是没有实现的,需要到他的子类AbstractPollingIoAcceptor查看,这个类中实现了该方法:

        AbstractPollingIoAcceptor$bindInternal

protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {
        // Create a bind request as a Future operation. When the selector
        // have handled the registration, it will signal this future.
        AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);

        // adds the Registration request to the queue for the Workers
        // to handle
        registerQueue.add(request);

        // creates the Acceptor instance and has the local
        // executor kick it off.
        startupAcceptor();

        // As we just started the acceptor, we have to unblock the select()
        // in order to process the bind request we just have added to the
        // registerQueue.
        try {
            lock.acquire();

            // Wait a bit to give a chance to the Acceptor thread to do the select()
            Thread.sleep(10);
            wakeup();
        } finally {
            lock.release();
        }

        // Now, we wait until this request is completed.
        request.awaitUninterruptibly();

        if (request.getException() != null) {
            throw request.getException();
        }

        // Update the local addresses.
        // setLocalAddresses() shouldn't be called from the worker thread
        // because of deadlock.
        Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();

        for (H handle : boundHandles.values()) {
            newLocalAddresses.add(localAddress(handle));
        }

        return newLocalAddresses;
    }

        首先创建了一个AcceptorOperationFuture类型的对象,当NioSocketAcceptor里面的Selector已经处理了该注册请求后,就会给AcceptorOperationFuture对象发送一个信号,至于什么地方会发送信号后面会讲到,接着会将创建的AcceptorOperationFuture对象添加到registerQueue中,他是一个AcceptorOperationFuture类型的队列,保存着我们所有注册到NioSocketAcceptor上面的服务端address组成的AcceptorOperationFuture,也就是说上面的requestQueue实际上存储的是服务端需要注册到NioSocketAcceptor里面的Selector的集合;接着第12行执行了startupAcceptor方法,我们来看看这个方法做了些什么;

private void startupAcceptor() throws InterruptedException {
        // If the acceptor is not ready, clear the queues
        // TODO : they should already be clean : do we have to do that ?
        if (!selectable) {
            registerQueue.clear();
            cancelQueue.clear();
        }

        // start the acceptor if not already started
        Acceptor acceptor = acceptorRef.get();

        if (acceptor == null) {
            lock.acquire();
            acceptor = new Acceptor();

            if (acceptorRef.compareAndSet(null, acceptor)) {
                executeWorker(acceptor);
            } else {
                lock.release();
            }
        }
    }
        这个方法关键是第10行或者14行,创建一个Acceptor类型的对象,Acceptor实现了Runnable接口,并且在第17行执行executeWorker方法,这个方法在AbstractPollingIoAcceptor中并没有实现,具体实现是在他的间接父类AbstractIoService中的,我们查看AbstractIoService中的executeWorker方法:

        AbstractIoService$executeWorker

protected final void executeWorker(Runnable worker) {
        executeWorker(worker, null);
    }

    protected final void executeWorker(Runnable worker, String suffix) {
        String actualThreadName = threadName;
        if (suffix != null) {
            actualThreadName = actualThreadName + '-' + suffix;
        }
        executor.execute(new NamePreservingRunnable(worker, actualThreadName));
    }
        可以看到实际上是首先将我们上面创建的Acceptor对象放到线程池executor里面,这里的executor线程池是我们在创建NioSocketAcceptor的时候创建的,他是CachedThreadPool类型的,随后执行exexcute方法,将该线程池运行起来,那么紧接着执行的就该是Acceptor的run方法了;

        AbstractPollingIoAcceptor$Acceptor$run()

public void run() {
            assert (acceptorRef.get() == this);

            int nHandles = 0;

            // Release the lock
            lock.release();

            while (selectable) {
                try {
                    // Detect if we have some keys ready to be processed
                    // The select() will be woke up if some new connection
                    // have occurred, or if the selector has been explicitly
                    // woke up
                    int selected = select();

                    // this actually sets the selector to OP_ACCEPT,
                    // and binds to the port on which this class will
                    // listen on
                    nHandles += registerHandles();

                    // Now, if the number of registred handles is 0, we can
                    // quit the loop: we don't have any socket listening
                    // for incoming connection.
                    if (nHandles == 0) {
                        acceptorRef.set(null);

                        if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
                            assert (acceptorRef.get() != this);
                            break;
                        }

                        if (!acceptorRef.compareAndSet(null, this)) {
                            assert (acceptorRef.get() != this);
                            break;
                        }

                        assert (acceptorRef.get() == this);
                    }

                    if (selected > 0) {
                        // We have some connection request, let's process
                        // them here.
                        processHandles(selectedHandles());
                    }

                    // check to see if any cancellation request has been made.
                    nHandles -= unregisterHandles();
                } catch (ClosedSelectorException cse) {
                    // If the selector has been closed, we can exit the loop
                    ExceptionMonitor.getInstance().exceptionCaught(cse);
                    break;
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        ExceptionMonitor.getInstance().exceptionCaught(e1);
                    }
                }
            }

            // Cleanup all the processors, and shutdown the acceptor.
            if (selectable && isDisposing()) {
                selectable = false;
                try {
                    if (createdProcessor) {
                        processor.dispose();
                    }
                } finally {
                    try {
                        synchronized (disposalLock) {
                            if (isDisposing()) {
                                destroy();
                            }
                        }
                    } catch (Exception e) {
                        ExceptionMonitor.getInstance().exceptionCaught(e);
                    } finally {
                        disposalFuture.setDone();
                    }
                }
            }
        }
        可以看到第9行首先判断 selectable的值是true还是false,这个值是在什么时候赋值的呢? 就是在AbstractPollingIoAcceptor的构造函数里面了,只要我们在NioSocketAcceptor里面创建了Selector对象之后就会将selectable的值设置为true,那么我们这里run方法里面的while循环将是死循环了,一直等待客户端的连接请求;第15行的select方法将处于阻塞状态,它实际上调用的就是我们Selector的select方法,一直等待着客户端的接入,在有客户端连接或者Selector被明确唤醒的情况下就会返回,返回结果大于0表示有客户端连接接入;接着执行第20行的registerHandles方法

        AbstractPollingIoAcceptor$registerHandles

private int registerHandles() {
        for (;;) {
            // The register queue contains the list of services to manage
            // in this acceptor.
            AcceptorOperationFuture future = registerQueue.poll();

            if (future == null) {
                return 0;
            }

            // We create a temporary map to store the bound handles,
            // as we may have to remove them all if there is an exception
            // during the sockets opening.
            Map<SocketAddress, H> newHandles = new ConcurrentHashMap<SocketAddress, H>();
            List<SocketAddress> localAddresses = future.getLocalAddresses();

            try {
                // Process all the addresses
                for (SocketAddress a : localAddresses) {
                    H handle = open(a);
                    newHandles.put(localAddress(handle), handle);
                }

                // Everything went ok, we can now update the map storing
                // all the bound sockets.
                boundHandles.putAll(newHandles);

                // and notify.
                future.setDone();
                return newHandles.size();
            } catch (Exception e) {
                // We store the exception in the future
                future.setException(e);
            } finally {
                // Roll back if failed to bind all addresses.
                if (future.getException() != null) {
                    for (H handle : newHandles.values()) {
                        try {
                            close(handle);
                        } catch (Exception e) {
                            ExceptionMonitor.getInstance().exceptionCaught(e);
                        }
                    }

                    // TODO : add some comment : what is the wakeup() waking up ?
                    wakeup();
                }
            }
        }
    }
        registerHandles方法主要用于 创建ServerSocketChannel,为通道创建ServerSocket并且为其绑定端口号,创建接收缓存区,并且为Selector注册OP_ACCEPT事件;

        首先第5行从我们的registerQueue服务端请求注册队队列中取出队首元素,第14行创建了一个临时的Map来存储我们已经绑定的请求地址对应的SocketAddress,为什么要这个临时的Map呢?原因就在于如果我们在Socket开启的状态下发生异常的话,我们需要移出掉这些已经绑定的请求地址,有点类似于数据库中的事务操作,如果有一个失败,那么就需要全部回滚,具体我们可以看到发生异常之后执行的是第33行代码,为future设置了异常,随后finally中进行了回滚操作;紧接着第15行获得可该AcceptorOperationFuture里面对应的SocketAddress列表,接着执行了第20行的open方法,为我们的每个SocketAddress创建一个ServerSocketChannel及其对应的ServerSocket,同时将通道注册到Selector上面,并且为当前通道注册OP_ACCEPT事件;我们来看看open方法,这个方法是在AbstractPollingIoAcceptor的子类NioSocketAcceptor中实现的;

        NioSocketAcceptor$open()

 protected ServerSocketChannel open(SocketAddress localAddress) throws Exception {
        // Creates the listening ServerSocket

        ServerSocketChannel channel = null;

        if (selectorProvider != null) {
            channel = selectorProvider.openServerSocketChannel();
        } else {
            channel = ServerSocketChannel.open();
        }

        boolean success = false;

        try {
            // This is a non blocking socket channel
            channel.configureBlocking(false);

            // Configure the server socket,
            ServerSocket socket = channel.socket();

            // Set the reuseAddress flag accordingly with the setting
            socket.setReuseAddress(isReuseAddress());

            // and bind.
            try {
                socket.bind(localAddress, getBacklog());
            } catch (IOException ioe) {
                // Add some info regarding the address we try to bind to the
                // message
                String newMessage = "Error while binding on " + localAddress + "\n" + "original message : "
                        + ioe.getMessage();
                Exception e = new IOException(newMessage);
                e.initCause(ioe.getCause());

                // And close the channel
                channel.close();

                throw e;
            }

            // Register the channel within the selector for ACCEPT event
            channel.register(selector, SelectionKey.OP_ACCEPT);
            success = true;
        } finally {
            if (!success) {
                close(channel);
            }
        }
        return channel;
    }
        可以看到这个open方法里面其实就是我们使用NIO的经典步骤了,首先创建一个ServerSocketChannel对象,接着将ServerSocketChannel通道设置为非阻塞式,根据当前通道创建一个ServerSocket对象,并且为当前ServerSocket绑定我们传入的参数SocketAddress,最后第42行把我们创建的通道注册到Selector选择器上面,同时注册OP_ACCEPT事件;

        open方法执行结束之后,registerHandles也算结束了,registerHandles中其他部分代码可以略过,至此,我们将服务端需要创建的ServerSocketChannel及其对应绑定了指定SocketAddress的ServerSocket注册到了Selector选择器中,同时注册了OP_ACCEPT事件;

        回到我们Acceptor里面的run方法,注意registerHandles方法的返回值实际上就是我们已经创建ServerSocketChannel的个数,接着就是执行第25行,如果我们创建的ServerSocketChannel个数为0的话,就会退出这个while死循环,因为我们没有任何ServerSocket来监听客户端连接的到来,避免资源的浪费;随后就是第41行,当有通道被选择的时候,selected的值将会是大于0的,那么就会执行第44行的processHandles方法,这个方法的参数是由selectedHandles获得的,他的实现是在NioSocketAcceptor里面的

        NioSocketAcceptor$selectedHandles

protected Iterator<ServerSocketChannel> selectedHandles() {
        return new ServerSocketChannelIterator(selector.selectedKeys());
    }
        可以看到实际上selectedHandles就是返回我们已经选中通道的集合而已了

        接下来我们看看processHandles做了些什么

        AbstractPollingIoAcceptor$processHandles

 private void processHandles(Iterator<H> handles) throws Exception {
            while (handles.hasNext()) {
                H handle = handles.next();
                handles.remove();

                // Associates a new created connection to a processor,
                // and get back a session
                S session = accept(processor, handle);

                if (session == null) {
                    continue;
                }

                initSession(session, null, null);

                // add the session to the SocketIoProcessor
                session.getProcessor().add(session);
            }
        }
    }
        这段代码相对来说比较短,我们仔细看看里面做了些什么,首先迭代我们的ServerSocketChannel集合,从中取出一个ServerSocketChannel对象,我这里把H的类型全部说成是ServerSocketChannel的原因在于我们主要分析的是MINA框架中关于Socket的这部分,因为MINA不仅仅支持Socket通信,同时支持UDP数据包通信,因而这里使用的是泛型实现的,在获得一个ServerSocketChannel对象之后,要注意将其从迭代器中删除,避免进行重复多次处理,接着执行第8行,创建一个IoSession对象出来,具体来讲我们这里创建的是NioSocketSession对象,调用的方法是accept,这个方法的第一个参数就是我们之前在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,默认情况下在他里面是会创建CPU个数+1个NioProcessor的,这个方法在 AbstractPollingIoAcceptor中是没有实现的,因此我们查看他的子类NioSocketAcceptor里面

        NioSocketAcceptor$accept()

protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {

        SelectionKey key = null;

        if (handle != null) {
            key = handle.keyFor(selector);
        }

        if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
            return null;
        }

        // accept the connection from the client
        SocketChannel ch = handle.accept();

        if (ch == null) {
            return null;
        }

        return new NioSocketSession(this, processor, ch);
    }

        这个方法里首先获得被选中ServerSocketChannel的key,接着对该key进行一系列的判断,接着第14行获取到和当前ServerSocketChannel有关联的SocketChannel,这里需要补充一点的就是ServerSocketChannel和Selector是通过SelectionKey来发生关联的,SelectionKey标志了我们当前ServerSocketChannel的状态,而如果说某一客户端想要和服务器某一端口服务发生关联的话,那么它实际上是和与该端口绑定的ServerSocketChannel发生联系的,因此我们就可以通过ServerSocketChannel获取与他有关联了客户端SocketChannel啦;最后执行第20行创建一个NioSocketSession对象,我们来看看他的构造函数;

        NioSocketSession$NioSocketSession()

public NioSocketSession(IoService service, IoProcessor<NioSession> processor, SocketChannel channel) {
        super(processor, service, channel);
        config = new SessionConfigImpl();
        this.config.setAll(service.getSessionConfig());
    }
        首先执行的是super的构造函数,其实就是NioSession的构造函数了,我们来看看

protected NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel) {
        super(service);
        this.channel = channel;
        this.processor = processor;
        filterChain = new DefaultIoFilterChain(this);
    }
        首先执行super的构造函数,实际上执行的是AbstractIoSession的构造函数,里面没有做多少事,我们不再展开讲,接着第5行创建了一个DefaultIoFilterChain对象出来,这个还是比较重要的,我们来看下里面做了什么事;

        DefaultIoFilterChain$DefaultIoFilterChain()

public DefaultIoFilterChain(AbstractIoSession session) {
        if (session == null) {
            throw new IllegalArgumentException("session");
        }

        this.session = session;
        head = new EntryImpl(null, null, "head", new HeadFilter());
        tail = new EntryImpl(head, null, "tail", new TailFilter());
        head.nextEntry = tail;
    }
        这个构造函数中为我们创建了两个EntryImpl类型的对象,分别封装的是HeadFilter和TailFilter对象,这里有必要说下DefaultIoFilterChain的作用了,在我们创建Session的时候,会为Session创建一个Filter责任链出来,那么责任链主要是干什么的呢?主要进行进行我们二进制与真正对象之间的转换啦,因为我们都知道在网络中传输的只能是字节,并不能传递对象,那么我们就需要字节和对象之间的转换,Filter链就是用来干这个的,当然你可以在客户端将要发送的数据通过Filter链来进行加密,在服务端再通过Filter链来进行解密,这个是完全可以的,既然是链嘛,就需要链头和链尾了;他们都会被封装到EntryImpl中,至于EntryImpl里面有什么我们就不贴出来了,主要就是prevEntry,nextEntry,nextFilter从名字上就能明显看出来主要是用于EntryImpl链拼接的实体罢了,有点类似于链表;

        到此呢,我们的NioSocketSession就创建成功啦,创建NioSocketSession其实主要就是在它里面创建一个IoFilter责任链出来,用于处理当前Session的一些编解码工作,这样我们的NioSocketAcceptor的accept方法就执行结束了,返回了一个NioSocketSession对象,继续回到AbstractPollingIoAcceptor里面的processHandles方法,在第8行创建完NioSocketSession之后,执行第17行,将我们的NioSocketSession对象放到NioProcessor中,具体实现过程见下:

        首先执行的是session的getProcessor方法,这里的session类型是NioSocketSession,所以我们去NioSocketSession里面查看getProcessor,你会发现它里面不存在这个方法,那就要去他的父类NioSession里面找了,果然我们找到了:

 public IoProcessor<NioSession> getProcessor() {
        return processor;
    }

        getProcessor里面的方法体非常简单,就是返回processor而已了,那么这个processor是在哪里赋值的呢?就是在创建NioSession的构造函数里面,我们在创建NioSocketSession的时候是会调用super来调用NioSession的构造函数的,也就是我们这里的processor就是我们在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,接下来调用的就是它里面的add方法啦:

 public final void add(S session) {
        getProcessor(session).add(session);
    }
        可以看到在SimpleIoProcessor里面的add方法里,首先执行的是getProcessor,从SimpleIoProcessor里面获得一个Processor对象出来,具体来讲这里获得到的Processor类型将是NioProcessor类型,我们看看getProcessor方法

private IoProcessor<S> getProcessor(S session) {
        IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);

        if (processor == null) {
            if (disposed || disposing) {
                throw new IllegalStateException("A disposed processor cannot be accessed.");
            }

            processor = pool[Math.abs((int) session.getId()) % pool.length];

            if (processor == null) {
                throw new IllegalStateException("A disposed processor cannot be accessed.");
            }

            session.setAttributeIfAbsent(PROCESSOR, processor);
        }

        return processor;
    }
        这个方法最关键的就是第9行,获取到当前session的id,对其取绝对值,并且对我们创建SimpleIoProcessor的时候创建的NioProcessor数组进行取余运算,获得数组中的一个NioProcessor对象,默认情况下这个数组的大小是CPU个数+1;最后第15行将当前Session的PROCESSOR属性设置为获取到的NioProcessor;

        那么到这里,实际上add操作执行的就是NioProcessor的add操作啦,我们查看NioProcessor里面会发现不存在这个方法,那么需要去他的父类AbstractPollingIoProcessor查看,代码见下:

        AbstractPollingIoProcessor$add()

public final void add(S session) {
        if (disposed || disposing) {
            throw new IllegalStateException("Already disposed.");
        }

        // Adds the session to the newSession queue and starts the worker
        newSessions.add(session);
        startupProcessor();
    }
        将当前NioSocketSession添加到newSession里面,这里的newSessions实际上就是NioSocketSession队列,就是我们当前NioProcessor需要处理的NioSocketSession所组成的集合了,为什么还要这个集合呢?道理很简单嘛,刚刚你在通过getProcessor方法为NioSocketSession设置处理他的NioPrrocessor的时候,采用的方法是通过session的id对包含NioProcessor对象的数组进行取模运算的,这肯定就不能避免多个NioSocketSession同时都需要一个NioProcessor来处理的情况了,那么为了保存这些需要NioProcessor处理的NioSocketSession,自然需要一个队列来存储了;

        紧接着执行了startupProcessor方法,如果你还记得上面的源码分析过程的话,会发现上面有调用过startupAcceptor方法,这两个方法不同之处在于一个是用于开启Processor线程执行它里面NioSocketSession请求的,一个是用于开启Acceptor来进行ServerSocketChannel的事件注册的,并且startupAcceptor只会执行一次,而startupProcessor会执行多次,默认情况下最多执行CPU个数+1次;

        我们来看看startupProcessor方法:

        AbstractPollingIoProcessor$startupProcessor

private void startupProcessor() {
        Processor processor = processorRef.get();

        if (processor == null) {
            processor = new Processor();

            if (processorRef.compareAndSet(null, processor)) {
                executor.execute(new NamePreservingRunnable(processor, threadName));
            }
        }

        // Just stop the select() and start it again, so that the processor
        // can be activated immediately.
        wakeup();
    }
        这个方法首先就是创建了一个Processor对象他实现了Runnable接口,随后调用executor的execute方法,将封装成NamePreservingRunnable的Processor放入线程池中,executor是CachedThreadPool类型的线程池,那么接下来就是执行Processor线程的run方法了:

public void run() {
            assert (processorRef.get() == this);

            int nSessions = 0;
            lastIdleCheckTime = System.currentTimeMillis();

            for (;;) {
                try {
                    // This select has a timeout so that we can manage
                    // idle session when we get out of the select every
                    // second. (note : this is a hack to avoid creating
                    // a dedicated thread).
                    long t0 = System.currentTimeMillis();
                    int selected = select(SELECT_TIMEOUT);
                    long t1 = System.currentTimeMillis();
                    long delta = (t1 - t0);

                    if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {
                        // Last chance : the select() may have been
                        // interrupted because we have had an closed channel.
                        if (isBrokenConnection()) {
                            LOG.warn("Broken connection");
                        } else {
                            LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
                            // Ok, we are hit by the nasty epoll
                            // spinning.
                            // Basically, there is a race condition
                            // which causes a closing file descriptor not to be
                            // considered as available as a selected channel,
                            // but
                            // it stopped the select. The next time we will
                            // call select(), it will exit immediately for the
                            // same
                            // reason, and do so forever, consuming 100%
                            // CPU.
                            // We have to destroy the selector, and
                            // register all the socket on a new one.
                            registerNewSelector();
                        }
                    }

                    // Manage newly created session first
                    nSessions += handleNewSessions();

                    updateTrafficMask();

                    // Now, if we have had some incoming or outgoing events,
                    // deal with them
                    if (selected > 0) {
                        // LOG.debug("Processing ..."); // This log hurts one of
                        // the MDCFilter test...
                        process();
                    }

                    // Write the pending requests
                    long currentTime = System.currentTimeMillis();
                    flush(currentTime);

                    // And manage removed sessions
                    nSessions -= removeSessions();

                    // Last, not least, send Idle events to the idle sessions
                    notifyIdleSessions(currentTime);

                    // Get a chance to exit the infinite loop if there are no
                    // more sessions on this Processor
                    if (nSessions == 0) {
                        processorRef.set(null);

                        if (newSessions.isEmpty() && isSelectorEmpty()) {
                            // newSessions.add() precedes startupProcessor
                            assert (processorRef.get() != this);
                            break;
                        }

                        assert (processorRef.get() != this);

                        if (!processorRef.compareAndSet(null, this)) {
                            // startupProcessor won race, so must exit processor
                            assert (processorRef.get() != this);
                            break;
                        }

                        assert (processorRef.get() == this);
                    }

                    // Disconnect all sessions immediately if disposal has been
                    // requested so that we exit this loop eventually.
                    if (isDisposing()) {
                        boolean hasKeys = false;
                        
                        for (Iterator<S> i = allSessions(); i.hasNext();) {
                            IoSession session = i.next();
                            
                            if (session.isActive()) {
                                scheduleRemove((S)session);
                                hasKeys = true;
                            }
                        }

                        if (hasKeys) {
                            wakeup();
                        }
                    }
                } catch (ClosedSelectorException cse) {
                    // If the selector has been closed, we can exit the loop
                    // But first, dump a stack trace
                    ExceptionMonitor.getInstance().exceptionCaught(cse);
                    break;
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        ExceptionMonitor.getInstance().exceptionCaught(e1);
                    }
                }
            }

            try {
                synchronized (disposalLock) {
                    if (disposing) {
                        doDispose();
                    }
                }
            } catch (Exception e) {
                ExceptionMonitor.getInstance().exceptionCaught(e);
            } finally {
                disposalFuture.setValue(true);
            }
        }
    }
        和Acceptor的run方法类似,同样存在一个死循环,第14行调用了Selector的select方法,但是和之前Acceptor中调用的select方法不同,我们这里调用的是有参数的select方法,这种方式会让我们的选择器每隔SELECT_TIMEOUT被唤醒一次,让他进行重新选择,目的就是为了管理空闲的NioSocketSession,而使用无参的select的话会一直阻塞下去,直到出现需要的事件为止;接着第43行执行了handleNewSessions方法

  private int handleNewSessions() {
        int addedSessions = 0;

        for (S session = newSessions.poll(); session != null; session = newSessions.poll()) {
            if (addNow(session)) {
                // A new session has been created
                addedSessions++;
            }
        }

        return addedSessions;
    }
        可以看到通过for循环不停的poll出队列中存在的NioSocketSession对象,同时调用addNow方法对当前NioSocketSession中对应的SocketChannel进行OP_READ操作的注册,具体我们可以看看addNow方法:

        AbstractPollingIoProcessor$addNow()

 private boolean addNow(S session) {
        boolean registered = false;

        try {
            init(session);
            registered = true;

            // Build the filter chain of this session.
            IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
            chainBuilder.buildFilterChain(session.getFilterChain());

            // DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here
            // in AbstractIoFilterChain.fireSessionOpened().
            // Propagate the SESSION_CREATED event up to the chain
            IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners();
            listeners.fireSessionCreated(session);
        } catch (Exception e) {
            ExceptionMonitor.getInstance().exceptionCaught(e);

            try {
                destroy(session);
            } catch (Exception e1) {
                ExceptionMonitor.getInstance().exceptionCaught(e1);
            } finally {
                registered = false;
            }
        }

        return registered;
    }
        这个方法首先第5行执行了init方法,这个方法就是用来为当前NioSocketSession对应的SocketChannel注册OP_READ事件的,具体实现是在NioProcessor里面的:

        NioProcessor$init()

@Override
    protected void init(NioSession session) throws Exception {
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));
    }
        可以看到首先是获得当前NioSocketSession对应的SocketChannel对应,他是SelectableChannel的子类,接着将当前获得到的通道设置为非阻塞式,随后为其注册OP_READ事件;

        这样的话,addNow方法执行结束了,由于这篇篇幅已经比较长了,所以决定在下一篇继续分析,未完,请继续查看下一篇

     



 

       





      



     

 
 





  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值