(二)Mina源码解析之IoService

在mina中,IoService提供基础的I/O服务、管理IoSession,它是Mina框架中最重要的一部分,大部分基础的I/O操作都是在IoService的实现类中被处理的

图一

IoService有如下几个功能:

1、通信管理:在server端和client端处理数据的传输

2、管理过滤器链:管理过滤器链

3、调用Handler:当有新消息到来时调用Handler

4、统计管理:更新发送的消息数,发送的字节数等等

5、listeners管理:管理监听器

6、管理sessions:创建和删除session,空闲检测

server端IoService的类结构如下:

图二

通信管理

mina的工作原理如下:

图三

在服务端,bind一个端口后,会创建一个Acceptor线程负责监听是否有新的连接到来,新的连接到来之后封装成IoSession交给Processor线程来处理,Processor线程会监控是否有数据从已经建立的连接中传输到server并进行处理


一个简单的TCPServer如下所示:

public class MinaServer {
    private static Logger logger = LoggerFactory.getLogger(MinaServer.class);
    public static void main(String[] args) {
        NioSocketAcceptor acceptor = new NioSocketAcceptor();   //1
        acceptor.getSessionConfig().setBothIdleTime(10000);     //2
        acceptor.getSessionConfig().setMaxReadBufferSize(2048); //3
        acceptor.setHandler(new ServerHandler());               //4
        acceptor.getFilterChain().addLast("codec",              //5
                new ProtocolCodecFilter(
                        new TextLineCodecFactory(
                                Charset.forName("UTF-8"),
                                LineDelimiter.WINDOWS.getValue(),
                                LineDelimiter.WINDOWS.getValue())));
        try {
            acceptor.bind(new InetSocketAddress("127.0.0.1", 8866));//6
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        logger.debug("MinaServer 启动 {}","success");
    }
}
本篇文章主要讲解上述代码中的第1步和第6步,mina都做了哪些工作,先用一张顺序图来说明mina在第1步和第6步做了哪些工作,顺序图如下:


图四

首先创建一个NioSocketAcceptor对象

NioSocketAcceptor acceptor = new NioSocketAcceptor();   //1
在创建NioSocketAcceptor对象的时候先创建一个父类对象AbstractPollingIoAcceptor并传入两个参数,第一个参数是一个DefaultSocketSessionConfig类,mina所有的可配置信息都在这个对象中,我们通过acceptor.getSessionConfig()方法来进行配置,getSessionConfig()方法获得的对象就是现在创建的这个DefaultSocketSessionConfig对象。
public NioSocketAcceptor() {
        super(new DefaultSocketSessionConfig(), NioProcessor.class);
        ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
    }

创建父类对象时用到了子类对象NioSocketAcceptor传进来的NioProcess.class参数,该参数是一个Class对象,AbstractPollingIoAcceptor根据该参数创建一个SimpleIoProcessorPool对象

protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {
        this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass), true, null);
    }

SimpleIoProcess是IoProcessor的实现类,该类的主要功能是创建一个IoProcessor数组,该数组的大小为所在机器的处理器数量+1,如代码1处所示,在代码2处通过反射创建IoProcessor实例,在代码2以下又创建了固定数量的IoProcessor实例。

processor线程主要负责具体的IO读写操作和IoFilterChain、IoHandler的逻辑,建立多个processor可以充分利用多核的处理能力,图三中的多个processor就是在这里创建

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]; //1)创建IoProcess数组

        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);//2) 创建IoProcessor
                    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();
            }
        }
    }

创建完SimpleIoProcessorPool实例后在代码1处赋值给AbstractPollingIoAcceptor的processor属性

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;//1
        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);
                }
            }
        }
    }

创建完AbstractPollingIoAcceptor对象后会依次初始化父类对象AbstractIoAcceptor、AbstractIoService如图4所示


至此,NioSocketAcceptor对象就已经创建完毕了,创建完NioSocketAcceptor后一般要设置过滤器***Filter,处理器***Handler(这些在后面的文章中会介绍),绑定监听端口,本篇文章重点介绍mina在绑定端口时为我们做了哪些工作。

绑定端口的过程如图4所示,在第2.1.1.1步调用了startupAcceptor方法,具体代码如下,在代码1处创建了acceptor对象并在代码2处将acceptor交由线程池执行逻辑,这个线程池是在创建NioSocketAcceptor时创建的

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();
            }
        }
    }
Acceptor类的代码如下,while循环中的循环变量selectable在创建NioSocketAcceptor对象时被赋值selectable=true;所以while循环会一直进行,该类的功能就是判断是否有新的连接到来,如果有新的连接到来就调用processHandles方法将新的连接封装成IoSession,在代码1处将连接封装成了一个Session。

private class Acceptor implements Runnable {
        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) {//判断是否有新的连接到来
                        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();
                    }
                }
            }
        }

        /**
         * This method will process new sessions for the Worker class.  All
         * keys that have had their status updates as per the Selector.selectedKeys()
         * method will be processed here.  Only keys that are ready to accept
         * connections are handled here.
         * <p/>
         * Session objects are created by making new instances of SocketSessionImpl
         * and passing the session object to the SocketIoProcessor class.
         */
        @SuppressWarnings("unchecked")
        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);//②
            }
        }
    }
让我们看看是怎么将连接封装成session的,实际上就是创建了一个NioSocketSession对象,创建对象时将IoService对象、IoProcessor对象、SocketChannel对象传入作为NioSocketSession类的属性,从图3中可以看到,Acceptor线程将连接封装成IoSession后交给了IoProcessor线程来处理,还记得在创建NioSocketAcceptor对象时初始化了processor属性,并为该属性赋值了一个SimpleIoProcessorPool吗,在代码2处通过session.getProcessor()方法可以拿到SimpleIoProcessorPool对象的引用,让我们看看SimpleIoProcessorPool对象的add方法是怎么实现的

    @Override
    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);//①
    }
add方法的实现如下:

    public final void add(S session) {
        getProcessor(session).add(session);
    }
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;
    }
又调用了一次getProcessor方法,这个getProcessor方法和之前的getProcessor方法完全不是一回事,之前的getProcessor方法是为了拿到SimpleIoProcessorPool对象,这个getProcessor方法是SimpleIoProcessorPool对象的一个方法,该方法的作用是根据sessionId在选出一个processor,然后调用add方法,在add方法的代码1处调用了startupProcessor方法,在该方法中,创建了一个Processor对象,并将该对象放到线程池中启动

    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();//①
    }
    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主要负责具体的IO读写操作和执行后面的IoFilterChain和IoHandler逻辑

Processor类的实现如下:

 private class Processor implements Runnable {
        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);
            }
        }
    }
在代码1处将客户端创建的新连接以事件的形式通过过滤器链传给IoHandler的SessionCreated和SessionOpend方法,如果不是新的连接而是消息到达,则会走到代码2处,该方法会将消息以事件的形式通过过滤器传递给IoHandler的MessageReceived方法。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值