深入理解Apache Mina(5)---- 配置Mina的 執行緒模式

來源:互聯網
上載者:User

在Mina的使用中,線程池的配置一個比較關鍵的環節,同時它也是Mina效能提高的一個有效方法,在Mina的2.0以上版本中已經不再需要對Mina線程池的配置了,本系列文章都是基於當前的穩定版本Mina 1.1.7版來進行講述的,Mina的2.0以上版本現在還都是M(millestone,即裡程碑)版的,在1.5版本上2.0M版為穩定版本,但是在1.5+以上則為非穩定版本,所以,為了更好的進行討論和學習,還是基於Mina 1.1.7版本進行討論,如果使用Mina 2.0進行開發要注意JDK的版本問題,當然如果有能力的話也可以自行修改和編譯Mina的2.0版本,這裡對此就不再多說,使用2.0版本的同學可以不用理會本文的內容。

 

 

 

  上面的內容都是基於Apache Mina提供的文檔講述,如有需要,請自行尋找相關資料,在此不再贅述。

下面開始對Mina的執行緒模式的配置、使用、及ExcutorFilter的基本原理進行簡單的講解。

配置Mina的三種背景工作執行緒


在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。

由上面的內容我們可以知道在Mina中可以配置的線程數量只有I/O processor,對於每個IoService再建立其執行個體的時候可以配置該IoService的I/O processor的線程數量。在SokcetConnector和SocketAccpetor中I/O Processor的數量是由CPU的核心數+1來決定的。

他們的配置方式如下:   Java代碼 <SPAN><SPAN style="FONT-SIZE: small">        /***            * 配置SocketAcceptor監聽器的I/O Processor的線程的數量,            * 此處的I/O Processor的線程數量由CPU的核心數決定,但Acceptor            * 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個,            * Acceptor的線程數量不能配置。            * */           SocketAcceptor acceptor = new SocketAcceptor(Runtime.getRuntime()                    .availableProcessors() + 1, Executors.newCachedThreadPool());               /***            * 配置SocketConnector監聽器的I/O Processor的線程的數量,            * 此處的I/O Processor的線程數量由CPU的核心數決定,但SocketConnector            * 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個,            * SocketConnector的線程數量不能配置。            * */           SocketConnector connector = new SocketConnector(Runtime.getRuntime()                    .availableProcessors() + 1, Executors.newCachedThreadPool());        </SPAN></SPAN>  

        /***         * 配置SocketAcceptor監聽器的I/O Processor的線程的數量,         * 此處的I/O Processor的線程數量由CPU的核心數決定,但Acceptor         * 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個,         * Acceptor的線程數量不能配置。         * */        SocketAcceptor acceptor = new SocketAcceptor(Runtime.getRuntime()                .availableProcessors() + 1, Executors.newCachedThreadPool());        /***         * 配置SocketConnector監聽器的I/O Processor的線程的數量,         * 此處的I/O Processor的線程數量由CPU的核心數決定,但SocketConnector         * 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個,         * SocketConnector的線程數量不能配置。         * */        SocketConnector connector = new SocketConnector(Runtime.getRuntime()                .availableProcessors() + 1, Executors.newCachedThreadPool());    

  在上面的配置比較難以理解的地方就是Runtime.getRuntime().availableProcessors() + 1,它的意思就是由JVM根據系統的情況(即CPU的核心數)來決定IO Processor的線程的數量。雖然這個線程的數量是在SocketAcceptor /SocketConnector 的構造器中進行的,但是對於SocketAcceptor /SocketConnector自身的線程沒有影響,SocketAcceptor /SocketConnector的線程數量仍然為1。為SocketAcceptor /SocketConnector本身就封裝了IO Processor,SocketAcceptor /SocketConnector只是由一個單獨的線程來負責接收外部串連/向外部請求建立串連,當串連建立後,SocketAcceptor /SocketConnector會把資料收發的任務轉交I/O Processor的線程。這個在本系列文章的《IoFilter和IoHandler的區別和聯絡》中的圖示中可以看。

圖中清晰的顯示了IO Processor就是位於IoService和IoFilter之間,IoService負責和外部建立串連,而IoFilter則負責處理接收到的資料,IoProcessor則負責資料的收發工作。

關於配置IO Processor的線程數量還有一種比較“笨”的辦法,那就一個一個試,你可以根據你的PC的硬體情況從1開始,每次加1,然後得出IO Processor的最佳的線程的數量。但是這種方式個人建議最好不要用了,上面的方法足矣。配置方法如下: Java代碼 <SPAN><SPAN style="FONT-SIZE: small">//從1--N開始嘗試,N的最大數量為CPU核心數+1    SocketAcceptor acceptor = new SocketAcceptor(N, Executors.newCachedThreadPool());         </SPAN></SPAN>  

//從1--N開始嘗試,N的最大數量為CPU核心數+1SocketAcceptor acceptor = new SocketAcceptor(N, Executors.newCachedThreadPool());     

 

為Mina的IoFilterChain添加線程池

在Mina的API中提供了一個ExecutorFilter,該線程池實現了IoFilter介面,它可以作為一個IoFilter添加到IoFilterChain中,它的作用就是將I/O Processor中的事件通過其自身封裝的一個線程池來轉寄到下一個過濾器中。在沒有添加該執行緒模式時,I/O Processor的事件是通過方法來觸發的,然後轉寄給IoHandler。在沒有添加該線程池的時候,所有的事件都是在單線程模式下啟動並執行,也就是說有的事件和處理(IO Processor,IoHandler,IoFilter)都是運行在同一個線程上,這個線程就是IO Processor的線程,但是這個線程的數量受到CPU核心數的影響,因此系統的效能也直接受CPU核心數的影響。

比較複雜的應用一般都會用到該線程池,你可以根據你的需求在IoFilterchain中你可以添加任意數量的線程池,這些線程池可以組合成一個事件驅動(SEDA)的處理模型。對於一般的應用來說不是線程的數量越多越好,線程的數量越多可能會加劇CPU切換線程所耗費的時間,反而會影響系統的效能,因此,線程的數量需要根據實際的需要由小到大,逐步添加,知道找到適合你系統的最佳線程的數量。ExcutorFilter的配置過程如下: Java代碼 <SPAN><SPAN style="FONT-SIZE: small">SocketAcceptor acceptor = ...;      DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();      filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());         </SPAN></SPAN>  

SocketAcceptor acceptor = ...;  DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();  filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());     

 在配置該線程池的時候需要注意的一個問題是,當你使用自定的ProtocolCodecFactory時候一定要將線程池配置在該過濾器之後,如下所示: Java代碼 <SPAN><SPAN style="FONT-SIZE: small">DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();      // 和CPU綁定的操作配置在過濾器的前面    filterChainBuilder.addLast("codec", new ProtocolCodecFactory(...));      // 添加線程池      filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());        </SPAN></SPAN>  

DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();  // 和CPU綁定的操作配置在過濾器的前面filterChainBuilder.addLast("codec", new ProtocolCodecFactory(...));  // 添加線程池  filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());    

 因為你自己實現的ProtocolCodecFactory直接讀取和轉換的是位元據,這些資料都是由和CPU綁定的I/O Processor來讀取和發送的,因此為了不影響系統的效能,也應該將資料的編解碼操作綁定到I/O Processor線程中,因為在Java中建立和線程切換都是比較耗資源的,因此建議將ProtocolCodecFactory配置在ExecutorFilter的前面。關於ProtocolCodecFactory詳細講述會在後續的文檔中給出,此處就不多說了。


最後給出一個伺服器執行緒模式完整配置的例子,該例子和KFCClient一起配置使用,詳細代碼在附件中,此處只給出代碼的主要部分:        Java代碼 <SPAN><SPAN style="FONT-SIZE: small">SocketAddress address = new InetSocketAddress("localhost", 4321);            /***            * 配置SocketAcceptor監聽器的I/O Processor的線程的數量, 此處的I/O            * Processor的線程數量由CPU的核心數決定,但Acceptor 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個,            * Acceptor的線程數量不能配置。            * */           IoAcceptor acceptor = new SocketAcceptor(Runtime.getRuntime()                    .availableProcessors() + 1, Executors.newCachedThreadPool());               acceptor.getDefaultConfig().setThreadModel(ThreadModel.MANUAL);            // 配置資料的轉碼器            acceptor.getDefaultConfig().getFilterChain().addLast("codec",                    new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));               // 此處為你自己實現的轉碼器            // config.getFilterChain().addLast("codec", new            // ProtocolCodecFactory(...));               // 為IoFilterChain添加線程池            acceptor.getDefaultConfig().getFilterChain().addLast("threadPool",                    new ExecutorFilter(Executors.newCachedThreadPool()));            acceptor.getDefaultConfig().getFilterChain().addLast("logger",                    new LoggingFilter());            // 綁定伺服器連接埠            acceptor.bind(address, new KFCFoodPriceHandler());            System.out.println("  伺服器開始在 8000 連接埠監聽 .......");            // ==========================================//            // 此處為用戶端的I/O Processor線程數的配置,你可以模仿 //            // IoAcceptor配置來實現 //            // ==========================================//            /***            * 配置SocketConnector監聽器的I/O Processor的線程的數量, 此處的I/O            * Processor的線程數量由CPU的核心數決定,但SocketConnector            * 的線程數量只有一個,也就是接收用戶端串連的線程數只有一個, SocketConnector的線程數量不能配置。            * */           // SocketConnector connector = new SocketConnector(Runtime.getRuntime()            // .availableProcessors() + 1, Executors.newCachedThreadPool());        }   </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: small"> </SPAN></SPAN>  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.