MINA框架 中JAVA NIO處理主要邏輯的processor線程

下面是MINA接收非阻塞連接的主要邏輯

線程模型文章可以參考http://chinaestone.iteye.com/blog/435929

在Mina的NIO模式中有三種I/O工作線程(這三種線程模型只在NIO Socket中有效,在NIO數據包和虛擬管道中沒有,也不需要配置):
Acceptor thread 
該線程的作用是接收客戶端的連接,並將客戶端的連接導入到I/O processor線程模型中。所謂的I/O processor線程模型就是Mina的I/O processor thread。Acceptor thread在調用了Acceptor.bind()方法後啓動。每個Acceptor只能創建一個Acceptor thread,該線程模型不能配置,它由Mina自身提供。 

Connector thread 
該線程模型是客戶端的連接線程模型,它的作用和Acceptor thread類似,它將客戶端與服務器的連接導入到I/O processor線程模型中。同樣地,該線程模型也是由Mina的客戶端自動創建,該線程模型也不能進行配置。 

I/O processor thread 
該線程模型的主要作用就行接收和發送數據,所有的IO操作在服務器與客戶端的連接建立後,所有的數據的接收和發送都是有該線程模型來負責的,知道客戶端與服務器的連接關閉,該線程模型才停止工作。該線程模型可以由程序員根據需要進行配置。該線程模型默認的線程的數量爲cpu的核數+1。若你的cpu爲雙核的,則你的I/O processor 線程的最大數量爲3,同理若你的若你的cpu爲四核的,那麼你的I/O processor 線程的最大數量爲5。 


acceptor主要註冊op_accept事件,只要收到連接事件,便初始化新會話並將會話交給processor處理後序的工作,這是兩個不同的線程(池)

每個任務都是通過線程池來執行的


    /**
     * This class is called by the startupAcceptor() method and is
     * placed into a NamePreservingRunnable class.
     * It's a thread accepting incoming connections from clients.
     * The loop is stopped when all the bound handlers are unbound.
     */
    private class Acceptor implements Runnable {
        public void run() {
            assert (acceptorRef.get() == this);

            int nHandles = 0;

            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. 這裏處理新來的連接,創建會話,然後交給IoProcessor線程池處理
                        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
                    break;
                } catch (Throwable 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) {
                    break;
                }

                initSession(session, null, null);

                // add the session to the SocketIoProcessor
                session.getProcessor().add(session);//會話交給SocketIoProcessor管理
            }
        }
    }



這個線程主要處理Acceptor傳遞過來的會話的讀、寫、關閉事件,是AbstractPollingIoProcessor類的內部類

    /**
     * The main loop. This is the place in charge to poll負責輪詢 the Selector, and to
     * process the active sessions. It's done in
     * - handle the newly created sessions
     * -
     */
    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 ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {
                        // Last chance : the select() may have been
                        // interrupted because we have had an closed channel.
                        if (isBrokenConnection()) {//由於有連接被關閉引起select中斷
                            LOG.warn("Broken connection");

                            // we can reselect immediately
                            // set back the flag to false
                            wakeupCalled.getAndSet(false);

                            continue;
                        } 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();
                        }

                        // Set back the flag to false
                        wakeupCalled.getAndSet(false);

                        // and continue the loop
                        continue;
                    }

                    // 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()) {//關閉所有會話
                        for (Iterator<S> i = allSessions(); i.hasNext();) {
                            scheduleRemove(i.next());//加入等關閉會話隊列
                        }

                        wakeup();//中斷select函數
                    }
                } catch (ClosedSelectorException cse) {
                    // If the selector has been closed, we can exit the loop
                    break;
                } catch (Throwable t) {
                    ExceptionMonitor.getInstance().exceptionCaught(t);

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

            try {
                synchronized (disposalLock) {
                    if (disposing) {
                        doDispose();
                    }
                }
            } catch (Throwable t) {
                ExceptionMonitor.getInstance().exceptionCaught(t);
            } finally {
                disposalFuture.setValue(true);
            }
        }
    }


發佈了180 篇原創文章 · 獲贊 52 · 訪問量 92萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章