NIO框架之MINA原始碼解析(二):mina核心引擎
NIO框架之MINA原始碼解析(一):背景
MINA的底層還是利用了jdk提供了nio功能,mina只是對nio進行封裝,包括MINA用的執行緒池都是jdk直接提供的。
MINA的server端主要有accept、processor、session三部分組成的。其中accept主要負責在指定的埠監聽,若有新連線則建立一個新的session;processor則負責處理session對應的傳送資料和接收資料並呼叫上層處理;session則快取當前連線資料。
MINA採用了執行緒懶啟動的技術,即最少啟動執行緒,在MINA server啟動的時候,只有一個執行緒-accept,並且accept執行緒只有一個,在指定的埠進行監聽(可以同時監聽多個埠,mina可以繫結多埠)。
1、acceptor
先看下acceptor的主要類圖吧。
mina server的啟動入口是在NioSocketAcceptor.bind(InetSocketAddress)或者NioSocketAcceptor.bind(SocketAddress...)方法, acceptor.bind(new InetSocketAddress(1234));
然後會呼叫AbstractPollingIoAcceptor.bindInternal(List<? extends SocketAddress>)方法,在bindInternal方法裡面會呼叫startupAcceptor()方法提交一個accept執行緒到執行緒池裡面(只提交一次),並初始化acceptor端的Selector,就這樣一個acceptor執行緒啟動了。
acceptor端業務相對簡單,相當於在當前Selector裡面監聽acceptor事件,處理新連線並新建一個session放到對應的processor裡面。
acceptor 程式碼,很簡單。
private class Acceptor implements Runnable {
public void run() {
assert (acceptorRef.get() == this);
int nHandles = 0;
// Release the lock
lock.release();
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.處理連線
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();
}
}
}
}
2、processor
processor顧名思義,就是進行IO處理,處理當前session的資料讀寫,並進行業務處理。
在mina server初始化的時候,會初始化一個processor池,通過NioSocketAcceptor的構造器傳入池的大小,預設是當前處理器的個數+1。
processor池裡面有一個jdk提供的 執行緒池 - Executors.newCachedThreadPool()。各個processor執行緒會引用此執行緒池,即每個processor執行緒都在這個執行緒池裡面執行。
在mina server實際處理時,每個processor相當於一個執行緒,輪流處理當前的session佇列裡面的資料(每個processor裡面的session相當於順序處理,共享一個執行緒)。
每個processor有一個Selector物件。
processor類圖
processor端的處理邏輯相對有點複雜,看下面的流程圖。
2、判斷當前Selector是否有讀寫事件;
3、若第2步有讀事件時,進入步驟4,若沒有的話,直接到第6步;
4、處理當前讀事件,並把處理後的資料放入到flush佇列裡面;
5、把第4步執行的結果flush到客戶端;
6、處理session,比如session idle時間等。
7、重新執行第1步,迴圈執行。
processor端程式碼。
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()) {
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 處理新新增進來的session,並註冊到當前processor的Selector讀事件
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...處理讀事件,並把結果放入flush佇列裡面
process();
}
// Write the pending requests 把flush佇列裡面的session的處理完的資料傳送給客戶端
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();
}
} 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);
}
}
}
3、session
session做為一個連線的具體物件,快取當前連線使用者的一些資訊。
session類圖
session物件是繫結在SelectableChannel的一個attach。
class NioProcessor
@Override
protected void init(NioSession session) throws Exception {
SelectableChannel ch = (SelectableChannel) session.getChannel();
ch.configureBlocking(false);
session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));//chanel 註冊讀事件,並把session當做一個attach輔導SelectableChannel裡面。
}
相關文章
- NIO框架之MINA原始碼解析(一):背景框架原始碼
- NIO框架之MINA原始碼解析(五):NIO超級陷阱和使用同步IO與MINA通訊框架原始碼
- NIO框架之MINA原始碼解析(三):底層通訊與責任鏈模式應用框架原始碼模式
- mina2原始碼解析原始碼
- NIO框架之MINA原始碼解析(四):粘包與斷包處理及編碼與解碼框架原始碼
- mina框架框架
- Android之Mina框架學習Android框架
- Java NIO框架Mina、Netty、Grizzly介紹與對比Java框架Netty
- 使用mina解析http協議的使用HTTP協議
- Mina--入門
- mina serial 串列埠串列埠
- Apache Mina實戰Apache
- 朝花夕拾之socket的基本使用以及mina框架簡單介紹框架
- Mina--結構分析
- 關於 mina netty activiemq RabbitMq nio 的比較 區別 應用NettyMQ
- mina 框架java服務端的搭建和通訊。框架Java服務端
- Android之Mina頻繁傳送心跳包Android
- Netty-Mina深入學習與對比(二)Netty
- Java - Apache Mina 簡單示例JavaApache
- mina 和 cindy 入門教程
- springboot整合mina報錯Spring Boot
- websocket連線mina被拒絕Web
- spring整合mina簡明教程Spring
- NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通訊實戰框架iOSNettyUDP
- Spring原始碼解析之BeanFactoryPostProcessor(二)Spring原始碼Bean
- Java集合框架之 Java HashMap 原始碼解析Java框架HashMap原始碼
- tcc分散式事務框架原始碼解析系列(二)之環境搭建分散式框架原始碼
- 使用mina自動化部署Rails應用AI
- Java - Apache Mina 自定義協議通訊JavaApache協議
- Java併發之Semaphore原始碼解析(二)Java原始碼
- dubbo原始碼解析之ExtensionLoader類(二)原始碼
- slate原始碼解析(二)- 基本框架與資料模型原始碼框架模型
- MySQL核心原始碼解讀-SQL解析之解析器淺析MySql原始碼
- spring mina整合(客戶端進行呼叫)Spring客戶端
- Apache MiNa + smack +openfilre 實現多人聊天室ApacheMac
- Ruby on Rails 終極部署方案 nginx+mina+pumaAINginx
- Java併發之ReentrantLock原始碼解析(二)JavaReentrantLock原始碼
- Java併發之ThreadPoolExecutor原始碼解析(二)Javathread原始碼