源码研读-mina多线程模型

来源:互联网 发布:初中女生内衣淘宝 编辑:程序博客网 时间:2024/05/16 14:48
         mina是目前很流行的一个网络应用框架,用以帮助用户构建高性能和高伸缩性的网络应用。称其为网络应用框架,主要是其极强的扩展性,支持包括http,ssh,ftp等在内的多种应用层协议。而它本身封装了底层的TCP, UDP等通信协议,使用也非常方便。本文的重点是分析源码,了解它的多线程模型。源码版本为apache-mina-2.0.7。
为了有助于理解,先简单介绍一下mina的整体架构。如下图所示,其分为三层。在通信层之上,是IOService,提供IO服务,管理连接信息,它包含两个实现,一个是IOAcceptor,位于服务器,一个是IOConnector,处在客户端。在IOService之上,是IOFilterChain,作为一个容器,包含一系列IOFilter,做一些通用的数据处理,像协议的转换,多线程,日志等。最上层是IOHandler,负责执行用户的业务逻辑。




(来源http://mina.apache.org/mina-project/userguide/ch2-basics/application-architecture.html)

       下面将按照服务器启动,连接监听,IO处理的顺序来对mina的多线程框架展开分析。
    protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {        ....        // cr eates the Acceptor instance and has the local        // executor kick it off.        startupAcceptor();        .....    }    private void startupAcceptor() throws InterruptedException {        ......        // 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();            }        }    }    protected final void executeWorker(Runnable worker, String suffix) {        String actualThreadName = threadName;        if (suffix != null) {            actualThreadName = actualThreadName + '-' + suffix;        }        executor.execute(new NamePreservingRunnable(worker, actualThreadName));    }

        服务启动时,创建了一个Acceptor实例并在后台运行。Acceptor 主要用来监听用户的连接请求,看看代码:

    private class Acceptor implements Runnable {        public void run() {            ......            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) {                        ......                    }                    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) {                    ......                }            }            ......        }        /**         * 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);            }        }    }
        Acceptor在监听到有连接时,创建了一个Session,并把该Session添加到IOProcessor的队列中。IOProcessor用来处理读写请求。为了进一步提高系统的性能,这里的IOProcessor默认实现为SimpleIOProcessorPool,池的大小默认为系统内核数+1。SimpleIOProcessorPool选择一个IOProcessor来进一步处理读写请求,代码如下:   
    public final void add(S session) {        getProcessor(session).add(session);    }    /**     * Find the processor associated to a session. If it hasen't be stored into     * the session's attributes, pick a new processor and stores it.     */    @SuppressWarnings("unchecked")    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;    }
        可以看出SimpleIOProcessorPool只是以简单轮转的方式来选择一个IOProcessor来处理Session。对于复杂的应用,也可以针对自己应用的特点定制IOProcessor。另外,值得注意的是,该Session与一个IOProcessor绑在了一起,这样实际的读写请求就只会在同一个线程内,避免了线程之间的同步。继续看IOProcessor的处理:  
 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();    }    /**     * Starts the inner Processor, asking the executor to pick a thread in its     * pool. The Runnable will be renamed     */    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();    }
        Session先被加入到队列中,然后IOProcessor启动了一个新的线程(Processor)来处理队列中Session的IO请求。在收到读的请求之后,Processor会触发IOFilterChain来处理读到的数据。IOFilterChain按拦截器模式设计,支持扩展。而mina框架本身提供了一个ExecutorFilter来支持在应用层实现多线程处理。这样就避免了应用层的阻塞妨碍到IO的读写。
    public final void messageReceived(NextFilter nextFilter, IoSession session, Object message) {        if (eventTypes.contains(IoEventType.MESSAGE_RECEIVED)) {            IoFilterEvent event = new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message);            fireEvent(event);        } else {            nextFilter.messageReceived(session, message);        }    }    protected void fireEvent(IoFilterEvent event) {        executor.execute(event);    }
        注意在这里会首先检查当前的事件是否已被用户指定,只有已指定的事件才会并发处理。所以并法控制需要用户谨慎地选择。当然用户也可以实现自己的并发策略,类似之前的IO处理线程,将同一个session的数据绑定到一个线程。
        由此看来,mina对多线程的控制是非常精细的,尤其是IO处理线程与连接监听线程的分离,设计相当巧妙,值得学习。