MINA框架是一个基于NIO的网络层框架,有着高并发,高扩展性的特点。最近刚好一个项目再用它,本着学习的目的,研究一下MINA框架的源码。
MINA在网络层对服务端以及客户端都进行了封装,本文主要从服务端开始学习下整个流程的搭建过程。
- 一、NIO服务端实现
在服务器端,一个简单的NIO实现如下:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NIOServerImpl {
public static void main(String[] args){
try {
//服务端通道注册
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(5222));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//开始监听
SocketChannel socketChannel = null;
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while(true){
int readyCount = selector.select();
if(readyCount == 0)
continue;
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey selectionKey = it.next();
if(selectionKey.isAcceptable()){
serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if(selectionKey.isReadable()){
socketChannel = (SocketChannel)selectionKey.channel();
readBuffer.clear();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int byteCount = socketChannel.read(readBuffer);
while(byteCount != -1){
readBuffer.flip();
byte[] bytes = new byte[1024];
while(readBuffer.hasRemaining()){
readBuffer.get(bytes, 0, bytes.length);
baos.write(bytes);
}
readBuffer.clear();
byteCount = socketChannel.read(readBuffer);
}
} else {
if(socketChannel != null)
socketChannel.close();
}
it.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
首先进行通道注册,然后开始进行监听。每当有新的事件过来时,通过Selector返回就绪的通道数,进行对应事件的操作(接收请求或者进行读写请求)。
二、MINA框架服务端流程
AbstractIoService类实现IoService接口提供基本的IO服务以及Session的管理。在AbstractIoService类中有一个Executor的实例,用于建立客户端以及服务端的线程池(监听或者连接),当服务端进行监听时,则启动以一个线程去执行监听任务。
NioSocketAcceptor类通过使用NIO的API完成服务端监听的建立。当创建一个NioSocketAcceptor实例时,完成IoProcessor实例以及Session会话的配置
public NioSocketAcceptor() {
super(new DefaultSocketSessionConfig(), NioProcessor.class);
((DefaultSocketSessionConfig) getSessionConfig()).init(this);
}
如果没有提供Executor的实例,则使用父类提供的Executor实例。
protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor){
......
if (executor == null) {
this.executor = Executors.newCachedThreadPool();
createdExecutor = true;
} else {
this.executor = executor;
createdExecutor = false;
}
}
NioSocketAcceptor提供了一个Selector,服务于服务端通道的选择,初始化同样在NioSocketAcceptor实例创建的时候。
在父类AbstractPollingIoAcceptor构造时调用init方法
private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,boolean createdProcessor, SelectorProvider selectorProvider){
......
try {
// Initialize the selector
init(selectorProvider);
// The selector is now ready, we can switch the
// flag to true so that incoming connection can be accepted
selectable = true;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeIoException("Failed to initialize.", e);
} finally {
if (!selectable) {
try {
destroy();
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
}
}
}
protected void init(SelectorProvider selectorProvider) throws Exception {
this.selectorProvider = selectorProvider;
if (selectorProvider == null) {
selector = Selector.open();
} else {
selector = selectorProvider.openSelector();
}
}
完成Selector实例的创建后,则开始进行Channel的创建以及注册。
Channel创建以及注册的方法在NioSocketAcceptor中实现:
protected ServerSocketChannel open(SocketAddress localAddress) throws Exception {
// Creates the listening ServerSocket
ServerSocketChannel channel = null;
if (selectorProvider != null) {
channel = selectorProvider.openServerSocketChannel();
} else {
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.
try {
socket.bind(localAddress, getBacklog());
} catch (IOException ioe) {
// Add some info regarding the address we try to bind to the
// message
String newMessage = "Error while binding on " + localAddress + "\n" + "original message : "
+ ioe.getMessage();
Exception e = new IOException(newMessage);
e.initCause(ioe.getCause());
// And close the channel
channel.close();
throw e;
}
// Register the channel within the selector for ACCEPT event
channel.register(selector, SelectionKey.OP_ACCEPT);
success = true;
} finally {
if (!success) {
close(channel);
}
}
return channel;
}
上述方法完成ServerSocketChannel的建立并进行注册。MINA可以注册多个通道,也就是说可以同时绑定多个IP和端口。
protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {
SelectionKey key = null;
if (handle != null) {
key = handle.keyFor(selector);
}
if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
return null;
}
// accept the connection from the client
SocketChannel ch = handle.accept();
if (ch == null) {
return null;
}
return new NioSocketSession(this, processor, ch);
}
MINA使用bind方法完成上述过程,NioSocketAcceptor没有此方法,主要通过继承父类AbstractIoAcceptor的bind方式来实现。bing过程中,首先将地址传递进去,然后调用AbstractIoAcceptor的bindInternal方法进行下一步操作。
bindInternal方法的实现在AbstractPollingIoAcceptor类中,
protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {
AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
//将要进行注册的地址加入到并发队列中
registerQueue.add(request);
//开始进行通道注册以及监听
startupAcceptor();
......
Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
for (H handle : boundHandles.values()) {
newLocalAddresses.add(localAddress(handle));
}
return newLocalAddresses;
}
在AbstractPollingIoAcceptor中,有一个Acceptor的内部类,用于监听客户端连接,同时,当没有任何Channel进行注册时,关闭监听服务。
private class Acceptor implements Runnable {
public void run() {
assert (acceptorRef.get() == this);
int nHandles = 0;
......
while (selectable) {
try {
//有多少通道就绪
int selected = select();
//服务端监听通道注册
nHandles += registerHandles();
......
if (selected > 0) {
//处理客户端的连接请求
processHandles(selectedHandles());
}
// 关闭未注册的通道
nHandles -= unregisterHandles();
} catch (ClosedSelectorException cse) {
ExceptionMonitor.getInstance().exceptionCaught(cse);
break;
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
ExceptionMonitor.getInstance().exceptionCaught(e1);
}
}
}
//停止服务
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();
}
}
}
}
以上run方法里面实现的流程和NIO实现的监听流程一样,只是在后续的处理过程中,MINA加入Processor以及Session来处理连接建立成功后的通道。
MINA服务器端的建立流程大致如上。