簡介
Apache mina是一個介於應用程序與網絡之間的NIO框架,它使程序員從繁瑣的網絡操作中解脫出來,花更多的時間在業務處理上。
如下圖所示,mina分爲三層
1、IOService層:處理IO操作
2、IOFilter層:過濾器鏈,日誌處理、字節變換、對象轉換等操作
3、IOHandler層:真正的處理業務邏輯的地方
IOService層根據不同的角色又分爲IOAcceptor和IOConnector,分別用於接受連接與請求連接操作。
IOAcceptor
上圖是IOAcceptor的類圖,IOAcceptor相當於是對ServerSocketChannel的封裝,最重要的兩個操作是綁定與接受連接,IOService接口中有多個重載的bind方法
public interface IoAcceptor extends IoService {
void bind() throws IOException;
void bind(SocketAddress localAddress) throws IOException;
void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException;
void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException;
}
其方法的實現在抽象類AbstractIOAcceptor的bind方法中,這個方法在做了參數檢查等操作後,將真正的綁定操作交給抽象方法bindInternal來完成。對於bindInternal有基於TCP/IP,UDP/IP,VMPipe三種實現,以TCP/IP爲例來看綁定過程 protected final Set<SocketAddress> bindInternal(
List<? extends SocketAddress> localAddresses) throws Exception {
// Create a bind request as a Future operation. When the selector
// have handled the registration, it will signal this future.
AcceptorOperationFuture request = new AcceptorOperationFuture(
localAddresses);
// adds the Registration request to the queue for the Workers
// to handle
registerQueue.add(request);
// creates the Acceptor instance and has the local
// executor kick it off.
startupAcceptor();
// As we just started the acceptor, we have to unblock the select()
// in order to process the bind request we just have added to the
// registerQueue.
wakeup();
// Now, we wait until this request is completed.
request.awaitUninterruptibly();
if (request.getException() != null) {
throw request.getException();
}
// Update the local addresses.
// setLocalAddresses() shouldn't be called from the worker thread
// because of deadlock.
Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
for (H handle:boundHandles.values()) {
newLocalAddresses.add(localAddress(handle));
}
return newLocalAddresses;
}
主要乾了以下幾件事情:
1、將綁定請求放入registerQueue中
2、啓動Acceptor,從Acceptor類的run方法可以看到,這一步會阻塞在Acceptor選擇器的選擇操作中
3、調用wakeup讓選擇器返回
4、等待請求處理完成,這一步會阻塞在ready變量中,當ready變量爲true時纔會返回,當接受連接後ready會被設置爲true.
現在重點看一下Acceptor的run方法
public void run() {
......
while (selectable) {
try {
int selected = select();
nHandles += registerHandles();
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) {
processHandles(selectedHandles());
}
// check to see if any cancellation request has been made.
nHandles -= unregisterHandles();
} catch (...) {
...
}
// Cleanup all the processors, and shutdown the acceptor. set ready=true
}
(1)、selector被wakeup喚醒後,調用registerHandles方法從registerQueue中取出請求依次調用open方法 protected ServerSocketChannel open(SocketAddress localAddress)
throws Exception {
// Creates the listening ServerSocket
ServerSocketChannel channel = ServerSocketChannel.open();
boolean success = false;
try {
// This is a non blocking socket channel
channel.configureBlocking(false);
// Configure the server socket,
ServerSocket socket = channel.socket();
// Set the reuseAddress flag accordingly with the setting
socket.setReuseAddress(isReuseAddress());
// and bind.
socket.bind(localAddress, getBacklog());
// Register the channel within the selector for ACCEPT event
channel.register(selector, SelectionKey.OP_ACCEPT);
success = true;
} finally {
if (!success) {
close(channel);
}
}
return channel;
}
open方法完成了ServerSocket的綁定和註冊(2)、從(1)中可以知道selector上註冊了ServerSocketChannel的OP_ACCEPT鍵,註冊後nHandles==0,selected==0,進行下一次循環,同樣是阻塞在select方法上
(3)、當連接到來時,select方法返回,selected>0,執行processHandles方法
private void processHandles(Iterator<H> handles) throws Exception {
while (handles.hasNext()) {
H handle = handles.next();
handles.remove();
// Associates a new created connection to a processor,
// and get back a session
S session = accept(processor, handle);
if (session == null) {
break;
}
initSession(session, null, null);
// add the session to the SocketIoProcessor
session.getProcessor().add(session);
}
}
該方法在完成真正的接受連接操作後,創建session並扔到processor中,後續的工作交給processor來完成。每個session中其實有一個SocketChannel,這個socketChannel實際上是被註冊到了processor的selector上。註冊代碼在NioProcessor類中可以找到 protected void init(NioSession session) throws Exception {
SelectableChannel ch = (SelectableChannel) session.getChannel();
ch.configureBlocking(false);
session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ,
session));
}
整個Acceptor的實現就講解完了,總結一下:Acceptor線程專門負責接受連接,在其上有一個selector,輪詢是否有連接建立上來,當有連接建立上來,調用ServerSocketChannel.accept方法來接受連接,這個方法返回一個session對象,然後將這個session對象加入processor中,由processor遍歷每個session來完成真正的IO操作。processor上也有一個selector與一個Processor線程,selector用於輪詢session,Processor線程處理每個session的IO操作。
IOConnector
IOConnector的類圖如下:
IOConnector的設計與IOAcceptor幾乎完全一樣,唯一不同的是與Acceptor線程對應的是Connector線程,在完成連接操作後也是扔了一個session對象到Processor中。
關於Processor與Session後續再分析......