MINA框架第一接口是org.apache.mina.core.service.IoService, 接收和发送两端的接口IoAcceptor, IoConnector都继承于它,它提供的方法主要有:
TransportMetadata getTransportMetadata();
底层传输的元数据,如网络服务提供者(NIO/APR/RXTX)等
void addListener(IoServiceListener listener);
void removeListener(IoServiceListener listener);
增加或删除相关联的监听事件
IoHandler getHandler();
void setHandler(IoHandler handler);
设置相关联的处理器
void setFilterChainBuilder(IoFilterChainBuilder builder);
IoFilterChainBuilder getFilterChainBuilder();
设置一个filter链,这个链是此service中所有IoSession相关联的IoFilterChain
等等.
IoAcceptor主要接收来自客户端的请求并处理,其核心方法就是
void bind(SocketAddress localAddress);
void unbind(SocketAddress localAddress);
IoConnector主要是连接客户端,反馈相应的信息,其核心方法是
ConnectFuture connect(SocketAddress remoteAddress);
抽象类AbstractIoService部分实现IoService,而抽象类AbstractIoAcceptor,AbstractIoConnector则都继承AbstractIoService,且分别实现对应的接口IoAcceptor, IoConnector.
IoAcceptor的实例是在MINA启动时产生的,只有一个,而其要处理的session则会借助IoProcessor实现多线程,其实现类SimpleIoProcessorPool在IoAcceptor初始化时new出来,形成个processor池,它在accept每绑定一个socketAddress时会在processor池中拉一个出来处理session(process的具体实现在AbstractPollingIoProcessor里,一个process对应多个session,每个session对应每次的请求,而每个process有一个Selector,通过其来实现多路复用).
下面是从MINA源码中取得的一个测试:
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.OrderedThreadPoolExecutor;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* TODO : Add documentation
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*
*/
public class MinaRegressionTest extends IoHandlerAdapter {
//private static final Logger logger = LoggerFactory.getLogger(MinaRegressionTest.class);
public static final int MSG_SIZE = 5000;
public static final int MSG_COUNT = 10;
private static final int PORT = 23234;
private static final int BUFFER_SIZE = 8192;
private static final int TIMEOUT = 10000;
public static final String OPEN = "open";
public SocketAcceptor acceptor;
public SocketConnector connector;
private final Object LOCK = new Object();
private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {
public Thread newThread(final Runnable r) {
return new Thread(null, r, "MinaThread", 64 * 1024);
}
};
private OrderedThreadPoolExecutor executor;
public static AtomicInteger sent = new AtomicInteger(0);
public MinaRegressionTest() throws IOException {
executor = new OrderedThreadPoolExecutor(
0,
1000,
60,
TimeUnit.SECONDS,
THREAD_FACTORY);
acceptor = new NioSocketAcceptor(Runtime.getRuntime().availableProcessors() + 1);
acceptor.setReuseAddress( true );
acceptor.getSessionConfig().setReceiveBufferSize(BUFFER_SIZE);
acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(executor));
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyProtocolCodecFactory()));
connector = new NioSocketConnector(Runtime.getRuntime().availableProcessors() + 1);
connector.setConnectTimeoutMillis(TIMEOUT);
connector.getSessionConfig().setSendBufferSize(BUFFER_SIZE);
connector.getSessionConfig().setReuseAddress( true );
}
public void connect() throws Exception {
final InetSocketAddress socketAddress = new InetSocketAddress("0.0.0.0", PORT);
acceptor.setHandler(new MyIoHandler(LOCK));
acceptor.bind(socketAddress);
connector.setHandler(this);
final IoFutureListener<ConnectFuture> listener = new IoFutureListener<ConnectFuture>() {
public void operationComplete(ConnectFuture future) {
try {System.out.println( "Write message to session " + future.getSession().getId() );
final IoSession s = future.getSession();
IoBuffer wb = IoBuffer.allocate(MSG_SIZE);
wb.put(new byte[MSG_SIZE]);
wb.flip();
s.write(wb);
} catch (Exception e) {
//System.out.println("Can't send message: {}", e.getMessage());
}
}
};
for (int i = 0; i < MSG_COUNT; i++) {
ConnectFuture future = connector.connect(socketAddress);
future.addListener(listener);
}
synchronized (LOCK) {
LOCK.wait(50000);
}
connector.dispose();
acceptor.unbind();
acceptor.dispose();
executor.shutdownNow();
System.out.println("Received: " + MyIoHandler.received.intValue());
System.out.println("Sent: " + sent.intValue());
System.out.println("FINISH");
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) {
if (!(cause instanceof IOException)) {
//logger.error("Exception: ", cause);
} else {
System.out.println("I/O error: " + cause.getMessage());
}
session.close(true);
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
sent.incrementAndGet();
}
public static void main(String[] args) throws Exception {
System.out.println("START");
new MinaRegressionTest().connect();
}
}
其中MinaRegressionTest构造函数其实就是MINA启动的代码,其初始化个NioSocketAcceptor,而方法connect()则是客户端要写的代码,设置handler,bind socket,最后在unbind().
具体来说,在NioSocketAcceptor初始化时会初始化一下信息:SimpleIoProcessorPool(初始化时会生成Runtime.getRuntime().availableProcessors() + 1大小的池),Executor线程池,IoServiceListener,IoFilterChain等.
而在执行bind()时,其最终是调用AbstractPollingIoAcceptor子类Acceptor的processHandles()方法来连接process和session,handle,进而处理,这里的实现就是基于Java NIO实现的Reactor模式的一个简单实现.
另看到其他一篇不错的关于MINA源码的文章(http://chillwarmoon.iteye.com ):
一 读源码时,首先要弄清楚代码所涉及到的重要模型及其之间的关系,从整体架构方面理解其组成。
1.对于IOService的实现NioSocketConnector和NioSocketAcceptor来说,都持有selector的引用,本身都有一个固定的线程池executor,用来执行Connector或者Acceptor任务。
2.除此之外,IoService还有对Processor池的引用,该池中的元素processor的数量是机器的cpu核数+1。
3.processor池中默认元素为NioProcessor对象,每个元素都有一个selector,而且有一个executor线程池,用来执行Processor任务。
二 分析框架的初始化方式,注意不要陷入技术细节,仍然是整体把握。
4.IoService初始化时,需要通过具体的实现提供的transportMetaData,来判断其中规定的session配置类(例如:DefaultSocketSessionConfig)是否来自接口SessionConfig。
5.IoService初始化时,建立与监听器容器IoServiceListenerSupport的双向关联,注册匿名内部实现serviceActivationListener到监听器容器。
PS:Mina框架比起Tomcat源码来说简单很多,Tomcat启动逻辑就花了很长时间来读。
三 要充分利用eclipse中提供给我们查找源码的方法,例如Ctrl+T,Ctrl+H,Ctrl+.,Alt+Shift+o等等。
四 框架初始化后,根据框架的使用方式,来逐步分析代码。
6.关键是对Session的理解:
持有对IoService,Processor池,SocketChannel,SessionConfig和IoService.IoHandler的引用。
在初始化session时,将config配置到了SocketChannel中。
深入理解:
在每次connect后,都会建立与之对应的session(session与channel是1:1关系),而且每个session都加入了processor中,但是都是持有的对一个IoHandler的引用。
将session加入到processor池:
该processor元素的选取是依次在池中选择的。然后将该processor元素加入到了session的attribute中。
也就是说,不同的session可能加入到池中的一个processor元素,一个processor元素有一个selector和一个Processor线程,该线程可以对processor元素对应的这些session进行处理。
7.Processor线程:
(1)如果有newSession,则将channel注册事件SelectionKey.OP_READ到该processor元素的selector
(2)将getFilterChainBuilder中的链节点加入到该session的链中
(3)fire相应的SessionCreate和SessionOpen的chain节点
(4)如果SelectionKey.OP_READ准备好,则read数据,如果session对应的发送队列有数据,则write数据
如果建立的session都放在了一个processor元素里,则handler只能够被一个线程访问,否则handler可能会被多个线程执行。