1主要流程
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuYXTid0MzUDM4YTMyMTMfBzLclTMvwVMxETMwIzLcRnbl1GajFGd0F2LcRXZu5ibkN3YukGavw1LcpDc0RHaiojIsJye.gif)
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用于與服務端建立連接配接,每連接配接一個服務端就建立一個線程。這兩種線程都是通過線程池建立的,我們可以在建構對象的時候就指定線程池類型:
[java]
publicNioSocketAcceptor(Executor executor, IoProcessor processor) {}
publicNioSocketConnector(Executor executor, IoProcessor processor) {}
此類線程池的構造在源代碼中為(AbstractIoService第168行):
[java]
protectedAbstractIoService(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,我們可以更改線程池的大小:
[java]
IoConnector connector =newNioSocketConnector(9);
IoAcceptor acceptor = newNioSocketAcceptor(9);
如上就把IoProcessor線程池的大小改為9個。
IoProcessor線程池的預設大小在源代碼中的定義(SimpleIoProcessorPool第82行):
Java代碼
private static final int DEFAULT_SIZE = Runtime.getRuntime().availableProcessors() + 1;
IoProcessor線程池的構造在源代碼中為(SimpleIoProcessorPool第144行):
Java代碼
public SimpleIoProcessorPool(Class extends IoProcessor> processorType,
Executor executor, int size) {
//省略部分代碼
if (createdExecutor) {
this.executor = Executors.newCachedThreadPool();
} else {
this.executor = executor;
}
}
2.3IoHandler線程
當我們在過濾器鍊中沒有添加“threadPool”過濾器,則業務邏輯處理和IoProcessor使用同一個線程。如果設定了“threadPool”過濾器,則使用設定的線程池産生線程進行業務邏輯處理,過濾器的配置如下:
Java代碼
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來檢視線程:
Java代碼
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();
}
}
}
Java代碼
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為過濾器配置的線程池建立的線程,用來業務邏輯處理