1主要流程
read in data:
IO讀入(IoProcessor)---日志記錄、解碼、threadPool(IoFilter)---業務邏輯處理(IoHandler)
write out data:
業務邏輯處理(IoHandler)---日志記錄、編碼、threadPool(IoFilter)---IO寫出(IoProcessor)
由以上可以看出,IO讀入和IO寫出的過程是相反的。
2線程
這里主要討論一下Mina中的線程,使用線程,線程池可以提高性能,Mina中就使用了幾種線程:
- IoAcceptor/IoConnector線程
- IoProcessor線程
- IoHandler線程
2.1IoAcceptor/IoConnector線程
IoAcceptor用於監聽客戶端的連接,每監聽一個端口建立一個線程。IoConnector用於與服務端建立連接,每連接一個服務端就建立一個線程。這兩種線程都是通過線程池建立的,我們可以在構建對象的時候就指定線程池類型:
- public NioSocketAcceptor(Executor executor, IoProcessor<NioSession> processor) {}
- public NioSocketConnector(Executor executor, IoProcessor<NioSession> processor) {}
此類線程池的構造在源代碼中為(AbstractIoService第168行):
- protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor) {
- //省略部分代碼
- if (executor == null) {
- this.executor = Executors.newCachedThreadPool();
- createdExecutor = true;
- } else {
- this.executor = executor;
- createdExecutor = false;
- }
- }
由此可見默認的線程池類型為newCachedThreadPool,這是一個可根據需要創建新線程的線程池,在以前構造的線程可用時可以重用它們。
2.2IoProcessor線程
對於一個IoAcceptor或IoConnector線程對應一個IoProcessor線程用於IO的處理,這個IoProcessor線程從IoProcessor線程池中取出。IoProcessor線程池的大小默認為機器的CPU核數+1,例如雙核機器的IoProcessor的線程池大小默認為3,我們可以更改線程池的大小:
- IoConnector connector = new NioSocketConnector(9);
- IoAcceptor acceptor = new NioSocketAcceptor(9);
如上就把IoProcessor線程池的大小改為9個。
IoProcessor線程池的默認大小在源代碼中的定義(SimpleIoProcessorPool第82行):
- private static final int DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + 1;
IoProcessor線程池的構造在源代碼中為(SimpleIoProcessorPool第144行):
- public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType,
- Executor executor, int size) {
- //省略部分代碼
- if (createdExecutor) {
- this.executor = Executors.newCachedThreadPool();
- } else {
- this.executor = executor;
- }
- }
2.3IoHandler線程
當我們在過濾器鏈中沒有添加“threadPool”過濾器,則業務邏輯處理和IoProcessor使用同一個線程。如果設置了“threadPool”過濾器,則使用設置的線程池產生線程進行業務邏輯處理,過濾器的配置如下:
- acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
如上配置之后,IO處理和業務邏輯處理將會使用各自的線程池產生線程使用。如果你的應用每次處理請求的時間較長而又希望應用能夠有較好的響應性,那么最好是把處理業務邏輯的任務放到一個新的線程中去執行,而不是在 mina 框架創建的線程中去執行。
2.4各種線程的產生
- 當 IoAcceptor/IoConnector實例創建的時候,同時一個關聯在IoAcceptor/IoConnector上的IoProcessor線程池也被創建。
- 當IoAcceptor/IoConnector建立套接字(IoAcceptor 的bind()或者是IoConnector 的connect()方法被調用)時,從線程池中取出一個線程,監聽套接字端口。
- 當 IoAcceptor/IoConnector監聽到套接字上有連接請求時,建立IoSession 對象,從IoProcessor池中取出一個IoProcessor線程執行IO處理。
- 如若過濾器中配置了“threadPool”過濾器,則使用此線程池建立線程執行業務邏輯(IoHandler)處理,否則使用IoProcessor線程處理業務邏輯。
3線程查看
舉個例子通過jdk自帶工具jvisualvm來查看線程:
- public class MinaTest {
- protected static Logger logger = LoggerFactory.getLogger(MinaTest.class);
- private static int PORT = 9999;
- public static void main(String[] args) {
- try {
- // 創建一個非阻塞的server端的Socket
- IoAcceptor acceptor = new NioSocketAcceptor();
- // 設置過濾器
- acceptor.getFilterChain().addLast("logger", new LoggingFilter());
- acceptor.getFilterChain().addLast("codec",
- new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
- acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
- // 設置讀取數據的緩沖區大小
- acceptor.getSessionConfig().setReadBufferSize(2048);
- // 讀寫通道10秒內無操作進入空閑狀態
- acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
- // 綁定邏輯處理器
- acceptor.setHandler(new MinaServerHandler());
- // 綁定端口
- acceptor.bind(new InetSocketAddress(PORT));
- logger.info("服務端啟動成功... 端口號為:" + PORT);
- } catch (Exception e) {
- logger.error("服務端啟動異常....", e);
- e.printStackTrace();
- }
- }
- }
- public class MinaServerHandler extends IoHandlerAdapter {
- protected static Logger logger = LoggerFactory.getLogger(MinaServerHandler.class);
- public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
- logger.error("服務端發送異常...", cause);
- }
- public void messageReceived(IoSession session, Object message) throws Exception {
- String msg = message.toString();
- //如果是quit就關閉session退出
- if ("quit".equals(msg)) {
- session.close();
- }
- Date date = new Date();
- session.write(date.toString());
- }
- public void sessionCreated(IoSession session) throws Exception {
- logger.info("服務端與客戶端創建連接...");
- }
- }
- 運行MinaTest類,啟動服務器端。
- 開始->運行->cmd進入控制台 窗口。
- 輸入:telnet 127.0.0.1 9999
- 再重復2、3步驟2次。
- 對jvisualvm的線程視圖截圖如下:
- 通過以上步驟我們可以看出我們打開了一個服務器端口,並用3個客戶端進行連接,下面我們通過以上總結的知識來分析一下服務端產生的線程:
- NioSccketAcceptor為服務器端監聽端口9999通過線程池創建的一個線程。
- NioProcessor-1、NioProcessor-2、NioProcessor-3為IoProcessor線程池創建的線程,用來IO處理。
- pool-3-thread-1、pool-3-thread-2、pool-3-thread-3為過濾器配置的線程池創建的線程,用來業務邏輯處理