源码研读-mina多线程模型

本文将探讨Mina服务启动时的Acceptor机制,它在后台运行,负责监听并处理用户连接请求。通过对源码的研读,揭示了Mina如何实现高效的多线程模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         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处理线程与连接监听线程的分离,设计相当巧妙,值得学习。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值