Apache MINA 線程模型配置

1、禁止缺省的ThreadModel設置

  MINA2.0及以後版本已經沒有ThreadModel了,如果使用這些版本的話,可以跳過本節。
  ThreadModel設置是在MINA1.0以後引入的,但是使用ThreadModel增加了配置的複雜性,推薦禁止掉缺省的TheadModel配置。
IoAcceptor acceptor = ...;
IoServiceConfig acceptorConfig = acceptor.getDefaultConfig();
acceptorConfig.setThreadModel(ThreadModel.MANUAL);
注意在相關指南中,假定你已經如本節所說的禁止了ThreadModel的缺省配置。

2、配置I/O工作線程的數量

  這節只是NIO實現相關的,NIO數據包以及虛擬機管道等的實現沒有這個配置。

  在MINA的NIO實現中,有三種I/O工作線程:

>>Acceptor線程 接受進入連接,並且轉給I/O處理器線程來進行讀寫操作。每一個SocketAcceptor產生一個Acceptor線程,線程的數目不能配置。
>>Connector線程 嘗試連接遠程對等機,並且將成功的連接轉給I/O處理器線程來進行讀寫操作。每一個SocketConnector產生一個Connector線程,這個的數目也不可以配置。
>>I/O處理器線程 執行實際上的讀寫操作直到連接關閉。每一個SocketAcceptor或SocketConnector都產生它們自己的I/O處理線程。這個數目可以配置,缺省是1。

  因此,對於每個IoService,可以配置的就是I/O處理線程的數目。下面的代碼產生一個有四個I/O處理線程的SocketAcceptor。

IoAcceptor acceptor = new SocketAcceptor(4, Executors.newCachedThreadPool());

沒有單憑經驗來決定I/O處理線程數目的方法,大概可以從1開始增加。

IoAcceptor acceptor = new SocketAcceptor(Runtime.getRuntime().availableProcessors() + 1, Executors.newCachedThreadPool());

3、增加一個ExecutorFilter到IoFilterChain中

  ExecutorFilter是一個IoFilter,用於將進入的I/O事件轉到一個 java.util.concurrent.Executor實現。事件會從這個Executor轉到下一個IoFilter,通常是一個線程池。可以在 IoFilterChain的任何地方增加任意數目的ExecutorFilter,實現任何類型的線程模型,從簡單的線程池到複雜的SEDA。

  到現在爲止我們還沒有增加ExecutorFilter,如果沒有增加ExecutorFilter,事件會通過方法調用轉到一個 IoHandler,這意味着在IoHandler實現中的業務邏輯會在I/O處理線程裏運行。我們叫這種線程模型爲"單線程模型"。單線程模型可以用來就會低反應網絡應用程序,受CPU限制的業務邏輯(如,遊戲服務器)。

  典型的網絡應用需要一個ExecutorFilter插入到IoFilterChain中,因爲業務邏輯和I/O處理線程有不同的資源使用模式。如果你用IoHandler的實現來執行數據庫操作,而沒有增加一個ExecutorFilter的話,那麼,你整個服務器會在執行數據庫操作的時候鎖定,特別是數據庫性能低的時候。下面的例子配置一個IoService在一個新的IoSession建立時增加一個ExecutorFilter。

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

這裏要注意ExecutorFilter沒有管理特定的Executor的生命週期,當完成時,需要關閉所有特定Executor的工作線程。

ExecutorService executor = ...;
IoAcceptor acceptor = ...;
DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();
filterChainBuilder.addLast("threadPool", new ExecutorFilter(executor);
// Start the server.
acceptor.bind(...);
// Shut down the server.
acceptor.unbind(...);
executor.shutdown();

使用一個ExecutorFilter通常不意味着要用一個線程池,對於Executor的實現沒有任何限制。

4、應該把ExecutorFilter放在IoFilterChain的什麼地方

  這個要根據於具體應用的情況來定。如果一個應用有一個ProtocolCodecFilter實現和一個常用的有數據庫操作的IoHandler實現的話,那麼就建議在ProtocolCodecFilter實現的後面增加一個ExecutorFilter,這是因爲大部分的協議解碼實現的性能特性是受CPU限制的,和I/O處理線程是一樣的。

IoAcceptor acceptor = ...;
DefaultIoFilterChainBuilder filterChainBuilder = acceptor.getDefaultConfig().getFilterChain();
// Add CPU-bound job first,
filterChainBuilder.addLast("codec", new ProtocolCodecFactory(...));
// and then a thread pool.
filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool());

5、選擇IoService的線程池類型時要小心

  Executors.newCachedThreadPool()經常是IoService首選的。因爲如果使用其它類型的話,可能會對 IoService產生不可預知的性能方面的影響。一旦池中的所有線程都在使用中,IoService會在向池嘗試請求一個線程時開始鎖定,然後會出現一個奇怪的性能下降,這有時是很難跟蹤的。

6、不推薦IoServices和ExecutorFilters共享一個線程池

  你可以想讓IoServices和ExecutorFilters共享一個線程池,而不是一家一個。這個是不禁止的,但是會出現很多問題,在這種情況下,除非你爲IoServices建立一個緩衝線程池。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章