上一篇我们通过实例学习了MINA框架的用法,发现用起来还是挺方便的,就那么几步就可以了,本着学东西必知其原理的观念,决定看看MINA的源码实现,好了,我们开始吧!
MINA源码对于客户端和服务端来说基本上差别不是很大的,所以我计划主要还是分析服务端的源码,在正式分析之前,我们需要对MINA有一个整体的了解;
MINA中涉及到了这么几个对象:
IoService:用于提供连接,他是IoAcceptor和IoConnector的父接口;
IoBuffer:消息缓存区;
IoSession:在每一次连接建立成功之后都会创建一个IoSession对象出来,并且在创建该对象的时候创建一个IoFilter对象出来,通过IoSession的session id来为当前IoSession设置处理他的IoProcessor;
IoProcessor:用于检查是否有数据在通道上面进行读写,在我们创建Acceptor或者Connector的时候,默认会创建一个线程池,里面存储的就是IoProcessor线程,该线程里面是拥有自己的Selector的,这个是MINA为我们做的一点优化,我们通常使用NIO的话是只有一个Selector的,而MINA中的
IoFilter:用于定义拦截器,这些拦截器可以包括日志输出、数据编解码等等,只要用于二进制数据和对象之间的转换;
IoHandler:处于IoFilter的尾部,用于真正的业务逻辑处理,所以我们在使用MINA的时候是必须要提供IoHandler对象的,因为是靠他来进行真正业务处理的;
接下来我们看看上篇博客中我们用到的MINA中涉及到的这几个对象的类结构图:
NioSocketAcceptor类结构图:
NioSocketConnector类结构图:
NioSocketSession类结构图:
NioProcessor类结构图:
好了,开始我们真正的源码分析了(服务端);
首先我们通过NioSocketAcceptor acceptor = new NioSocketAcceptor();创建了一个NioSocketAcceptor对象出来,那我们就得看看NioSocketAcceptor的构造函数里面做了些什么事了;
NioSocketAcceptor$NioSocketAcceptor()
public NioSocketAcceptor() {
super(new DefaultSocketSessionConfig(), NioProcessor.class);
((DefaultSocketSessionConfig) getSessionConfig()).init(this);
}
可以看到首先调用了父类的构造函数,也就是AbstractPollingIoAcceptor的构造函数,并且传入了NioProcessor的Class对象,这里我们可以想象一下后面肯定会用这个NioProcessor的Class对象进行一些与反射有关的操作;AbstractPollingIoAcceptor$AbstractPollingIoAcceptor()
protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {
this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass), true, null);
}
可以看到实际上调用的是5个参数的构造函数,在看这个构造函数之前,我们看到第三个参数利用我们从NioSocketAcceptor构造函数中传进来的NioProcessor对象,创建了一个SimpleIoProcessorPool对象,我们来看看SimpleIoProcessorPool的构造函数;SimpleIoProcessorPool$SimpleIoProcessorPool()
public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType) {
this(processorType, null, DEFAULT_SIZE, null);
}
发现他接着调用的是SimpleIoProcessorPool四个参数的构造函数,并且添加了一个DEFAULT_SIZE参数,这个值的大小等于我们CPU的核数+1,这也是我们在创建NioSocketAcceptor的时候默认创建的NioProcessor的线程个数,来看看SimpleIoProcessorPool四个参数的构造函数:
public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, Executor executor, int size,
SelectorProvider selectorProvider) {
if (processorType == null) {
throw new IllegalArgumentException("processorType");
}
if (size <= 0) {
throw new IllegalArgumentException("size: " + size + " (expected: positive integer)");
}
// Create the executor if none is provided
createdExecutor = (executor == null);
if (createdExecutor) {
this.executor = Executors.newCachedThreadPool();
// Set a default reject handler
((ThreadPoolExecutor) this.executor).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
} else {
this.executor = executor;
}
pool = new IoProcessor[size];
boolean success = false;
Constructor<? extends IoProcessor<S>> processorConstructor = null;
boolean usesExecutorArg = true;
try {
// We create at least one processor
try {
try {
processorConstructor = processorType.getConstructor(ExecutorService.class);
pool[0] = processorConstructor.newInstance(this.executor);
} catch (NoSuchMethodException e1) {
// To the next step...
try {
if(selectorProvider==null) {
processorConstructor = processorType.getConstructor(Executor.class);
pool[0] = processorConstructor.newInstance(this.executor);
} else {
processorConstructor = processorType.getConstructor(Executor.class, SelectorProvider.class);
pool[0] = processorConstructor.newInstance(this.executor,selectorProvider);
}
} catch (NoSuchMethodException e2) {
// To the next step...
try {
processorConstructor = processorType.getConstructor();
usesExecutorArg = false;
pool[0] = processorConstructor.newInstance();
} catch (NoSuchMethodException e3) {
// To the next step...
}
}
}
} catch (RuntimeException re) {
LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage());
throw re;
} catch (Exception e) {
String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage();
LOGGER.error(msg, e);
throw new RuntimeIoException(msg, e);
}
if (processorConstructor == null) {
// Raise an exception if no proper constructor is found.
String msg = String.valueOf(processorType) + " must have a public constructor with one "
+ ExecutorService.class.getSimpleName() + " parameter, a public constructor with one "
+ Executor.class.getSimpleName() + " parameter or a public default constructor.";
LOGGER.error(msg);
throw new IllegalArgumentException(msg);
}
// Constructor found now use it for all subsequent instantiations
for (int i = 1; i < pool.length; i++) {
try {
if (usesExecutorArg) {
if(selectorProvider==null) {
pool[i] = processorConstructor.newInstance(this.executor);
} else {
pool[i] = processorConstructor.newInstance(this.executor, selectorProvider);
}
} else {
pool[i] = processorConstructor.newInstance();
}
} catch (Exception e) {
// Won't happen because it has been done previously
}
}
success = true;
} finally {
if (!success) {
dispose();
}
}
}
这段代码相对来说比较长,可以看到在第14行判断传入SimpleIoProcessorPool的executor是否为null,为null的话执行第15行,创建一个CachedThreadPool类型的线程池,随后在第32行通过反射获取到processorType参数为ExecutorService的构造函数,我们这里的processType实际上就是NioProcessor,随后33行通过反射创建一个NioProcessor对象出来,调用的是他的下面这个构造函数:
public NioProcessor(Executor executor) {
super(executor);
try {
// Open a new selector
selector = Selector.open();
} catch (IOException e) {
throw new RuntimeIoException("Failed to open a selector.", e);
}
}
可以注意到的是在SimpleIoProcessorPool里面有两种通过反射创建NioProcessor对象的方式,就是我们上面代码的第78和80这两种方式,两者的区别在于如果我们在创建SimpleIoProcessorPool的时候传入了SelectorProvider对象,那么NioProcessor里面的Selector将直接调用SelectorProvider的openSelector来获得,而如果没有传入SelectorProvider对象的话,NioProcessor里面的Selector将通过Selector.open方法获得;
到此,我们创建出来了CPU个数+1个NioProcessor,每个NioProcessor里面都会有一个Selector对象;
我们回到AbstractPollingIoAcceptor的构造函数
private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,
boolean createdProcessor, SelectorProvider selectorProvider) {
super(sessionConfig, executor);
if (processor == null) {
throw new IllegalArgumentException("processor");
}
this.processor = processor;
this.createdProcessor = createdProcessor;
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);
}
}
}
}
首先执行了super构造函数,这个构造函数实际上执行的是AbstractIoService的构造函数;
protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor) {
if (sessionConfig == null) {
throw new IllegalArgumentException("sessionConfig");
}
if (getTransportMetadata() == null) {
throw new IllegalArgumentException("TransportMetadata");
}
if (!getTransportMetadata().getSessionConfigType().isAssignableFrom(sessionConfig.getClass())) {
throw new IllegalArgumentException("sessionConfig type: " + sessionConfig.getClass() + " (expected: "
+ getTransportMetadata().getSessionConfigType() + ")");
}
// Create the listeners, and add a first listener : a activation listener
// for this service, which will give information on the service state.
listeners = new IoServiceListenerSupport(this);
listeners.add(serviceActivationListener);
// Stores the given session configuration
this.sessionConfig = sessionConfig;
// Make JVM load the exception monitor before some transports
// change the thread context class loader.
ExceptionMonitor.getInstance();
if (executor == null) {
this.executor = Executors.newCachedThreadPool();
createdExecutor = true;
} else {
this.executor = executor;
createdExecutor = false;
}
threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
}
这个构造函数会判断我们的executor是否为null,为null的话会创建一个CachedThreadPool出来,这里我们传入给AbstractPollingIoAcceptor的参数值为null,因此会创建一个Executor出来;
可以看到第14行执行了init方法,传入的参数是SelectorProvider类型对象,这个方法在AbstractPollingIoAcceptor里面并没有实现,因此查看AbstractPollingIoAcceptor的子类NioSocketAcceptor的init(SelectorProvider)方法
protected void init(SelectorProvider selectorProvider) throws Exception {
this.selectorProvider = selectorProvider;
if (selectorProvider == null) {
selector = Selector.open();
} else {
selector = selectorProvider.openSelector();
}
}
这个方法所做的事还是比较简单的,就是创建根据SelectorProvider是否为空创建Selector对象而已,注意这个Selector对象是属于NioSocketAcceptor的;
在init执行结束之后,AbstractPollingIoAcceptor构造函数第18行会将selectable设置为true,表示我们NioSocketAcceptor里面的Selector对象已经创建结束了,我们可以处理随后客户端到来的连接请求了;
至此,NioSocketAcceptor的构造方法执行结束了,在这个构造方法中为我们创建出了CPU个数+1个NioProcess对象,每个对象里面都包含一个Selector对象,同时也为NioSocketAcceptor创建了一个Selector对象,同时从上面可以发现我们的NioSocketAcceptor和SimpleIoProcessorPool里的线程池可以是同一个也可以不是同一个,具体就在你创建NioSocketAcceptor和SimpleIoProcessorPool是否传入同一个Executor就可以啦;
有了NioSocketAcceptor对象之后,我们通过有了NioSocketAcceptor的bind方法将他和某一个端口绑定起来,因此查看NioSocketAcceptor的bind方法,你会发现根本不存在,那么根据前面NioSocketAcceptor的类结构图,去他的父类AbstractPollingIoAcceptor查找,还是没有,那只能继续向上找,找到AbstractIoAcceptor里面,终于找到了;
AbstractIoAcceptor$bind()
public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
if (isDisposing()) {
throw new IllegalStateException("The Accpetor disposed is being disposed.");
}
if (localAddresses == null) {
throw new IllegalArgumentException("localAddresses");
}
List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
for (SocketAddress a : localAddresses) {
checkAddressType(a);
localAddressesCopy.add(a);
}
if (localAddressesCopy.isEmpty()) {
throw new IllegalArgumentException("localAddresses is empty.");
}
boolean activate = false;
synchronized (bindLock) {
synchronized (boundAddresses) {
if (boundAddresses.isEmpty()) {
activate = true;
}
}
if (getHandler() == null) {
throw new IllegalStateException("handler is not set.");
}
try {
Set<SocketAddress> addresses = bindInternal(localAddressesCopy);
synchronized (boundAddresses) {
boundAddresses.addAll(addresses);
}
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e);
}
}
if (activate) {
getListeners().fireServiceActivated();
}
}
不管你调用的是哪个bind方法,最后执行的都是这个bind方法,在这个方法中首先会进行迭代,将所有需要绑定的地址存储到localAddressesCopy里面,随后在第34行调用bindInternal方法进行绑定,这个方法在AbstractIoAcceptor里面是没有实现的,需要到他的子类AbstractPollingIoAcceptor查看,这个类中实现了该方法:
AbstractPollingIoAcceptor$bindInternal
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.
try {
lock.acquire();
// Wait a bit to give a chance to the Acceptor thread to do the select()
Thread.sleep(10);
wakeup();
} finally {
lock.release();
}
// 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;
}
首先创建了一个AcceptorOperationFuture类型的对象,当NioSocketAcceptor里面的Selector已经处理了该注册请求后,就会给AcceptorOperationFuture对象发送一个信号,至于什么地方会发送信号后面会讲到,接着会将创建的AcceptorOperationFuture对象添加到registerQueue中,他是一个AcceptorOperationFuture类型的队列,保存着我们所有注册到NioSocketAcceptor上面的服务端address组成的AcceptorOperationFuture,也就是说上面的requestQueue实际上存储的是服务端需要注册到NioSocketAcceptor里面的Selector的集合;接着第12行执行了startupAcceptor方法,我们来看看这个方法做了些什么;
private void startupAcceptor() throws InterruptedException {
// If the acceptor is not ready, clear the queues
// TODO : they should already be clean : do we have to do that ?
if (!selectable) {
registerQueue.clear();
cancelQueue.clear();
}
// start the acceptor if not already started
Acceptor acceptor = acceptorRef.get();
if (acceptor == null) {
lock.acquire();
acceptor = new Acceptor();
if (acceptorRef.compareAndSet(null, acceptor)) {
executeWorker(acceptor);
} else {
lock.release();
}
}
}
这个方法关键是第10行或者14行,创建一个Acceptor类型的对象,Acceptor实现了Runnable接口,并且在第17行执行executeWorker方法,这个方法在AbstractPollingIoAcceptor中并没有实现,具体实现是在他的间接父类AbstractIoService中的,我们查看AbstractIoService中的executeWorker方法:
AbstractIoService$executeWorker
protected final void executeWorker(Runnable worker) {
executeWorker(worker, null);
}
protected final void executeWorker(Runnable worker, String suffix) {
String actualThreadName = threadName;
if (suffix != null) {
actualThreadName = actualThreadName + '-' + suffix;
}
executor.execute(new NamePreservingRunnable(worker, actualThreadName));
}
可以看到实际上是首先将我们上面创建的Acceptor对象放到线程池executor里面,这里的executor线程池是我们在创建NioSocketAcceptor的时候创建的,他是CachedThreadPool类型的,随后执行exexcute方法,将该线程池运行起来,那么紧接着执行的就该是Acceptor的run方法了;
AbstractPollingIoAcceptor$Acceptor$run()
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
ExceptionMonitor.getInstance().exceptionCaught(cse);
break;
} catch (Exception 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();
}
}
}
}
可以看到第9行首先判断
selectable的值是true还是false,这个值是在什么时候赋值的呢?
就是在AbstractPollingIoAcceptor的构造函数里面了,只要我们在NioSocketAcceptor里面创建了Selector对象之后就会将selectable的值设置为true,那么我们这里run方法里面的while循环将是死循环了,一直等待客户端的连接请求;第15行的select方法将处于阻塞状态,它实际上调用的就是我们Selector的select方法,一直等待着客户端的接入,在有客户端连接或者Selector被明确唤醒的情况下就会返回,返回结果大于0表示有客户端连接接入;接着执行第20行的registerHandles方法
AbstractPollingIoAcceptor$registerHandles
private int registerHandles() {
for (;;) {
// The register queue contains the list of services to manage
// in this acceptor.
AcceptorOperationFuture future = registerQueue.poll();
if (future == null) {
return 0;
}
// We create a temporary map to store the bound handles,
// as we may have to remove them all if there is an exception
// during the sockets opening.
Map<SocketAddress, H> newHandles = new ConcurrentHashMap<SocketAddress, H>();
List<SocketAddress> localAddresses = future.getLocalAddresses();
try {
// Process all the addresses
for (SocketAddress a : localAddresses) {
H handle = open(a);
newHandles.put(localAddress(handle), handle);
}
// Everything went ok, we can now update the map storing
// all the bound sockets.
boundHandles.putAll(newHandles);
// and notify.
future.setDone();
return newHandles.size();
} catch (Exception e) {
// We store the exception in the future
future.setException(e);
} finally {
// Roll back if failed to bind all addresses.
if (future.getException() != null) {
for (H handle : newHandles.values()) {
try {
close(handle);
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
}
// TODO : add some comment : what is the wakeup() waking up ?
wakeup();
}
}
}
}
registerHandles方法主要用于
创建ServerSocketChannel,为通道创建ServerSocket并且为其绑定端口号,创建接收缓存区,并且为Selector注册OP_ACCEPT事件;
首先第5行从我们的registerQueue服务端请求注册队队列中取出队首元素,第14行创建了一个临时的Map来存储我们已经绑定的请求地址对应的SocketAddress,为什么要这个临时的Map呢?原因就在于如果我们在Socket开启的状态下发生异常的话,我们需要移出掉这些已经绑定的请求地址,有点类似于数据库中的事务操作,如果有一个失败,那么就需要全部回滚,具体我们可以看到发生异常之后执行的是第33行代码,为future设置了异常,随后finally中进行了回滚操作;紧接着第15行获得可该AcceptorOperationFuture里面对应的SocketAddress列表,接着执行了第20行的open方法,为我们的每个SocketAddress创建一个ServerSocketChannel及其对应的ServerSocket,同时将通道注册到Selector上面,并且为当前通道注册OP_ACCEPT事件;我们来看看open方法,这个方法是在AbstractPollingIoAcceptor的子类NioSocketAcceptor中实现的;
NioSocketAcceptor$open()
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;
}
可以看到这个open方法里面其实就是我们使用NIO的经典步骤了,首先创建一个ServerSocketChannel对象,接着将ServerSocketChannel通道设置为非阻塞式,根据当前通道创建一个ServerSocket对象,并且为当前ServerSocket绑定我们传入的参数SocketAddress,最后第42行把我们创建的通道注册到Selector选择器上面,同时注册OP_ACCEPT事件;
open方法执行结束之后,registerHandles也算结束了,registerHandles中其他部分代码可以略过,至此,我们将服务端需要创建的ServerSocketChannel及其对应绑定了指定SocketAddress的ServerSocket注册到了Selector选择器中,同时注册了OP_ACCEPT事件;
回到我们Acceptor里面的run方法,注意registerHandles方法的返回值实际上就是我们已经创建ServerSocketChannel的个数,接着就是执行第25行,如果我们创建的ServerSocketChannel个数为0的话,就会退出这个while死循环,因为我们没有任何ServerSocket来监听客户端连接的到来,避免资源的浪费;随后就是第41行,当有通道被选择的时候,selected的值将会是大于0的,那么就会执行第44行的processHandles方法,这个方法的参数是由selectedHandles获得的,他的实现是在NioSocketAcceptor里面的
NioSocketAcceptor$selectedHandles
protected Iterator<ServerSocketChannel> selectedHandles() {
return new ServerSocketChannelIterator(selector.selectedKeys());
}
可以看到实际上selectedHandles就是返回我们已经选中通道的集合而已了
接下来我们看看processHandles做了些什么
AbstractPollingIoAcceptor$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) {
continue;
}
initSession(session, null, null);
// add the session to the SocketIoProcessor
session.getProcessor().add(session);
}
}
}
这段代码相对来说比较短,我们仔细看看里面做了些什么,首先迭代我们的ServerSocketChannel集合,从中取出一个ServerSocketChannel对象,我这里把H的类型全部说成是ServerSocketChannel的原因在于我们主要分析的是MINA框架中关于Socket的这部分,因为MINA不仅仅支持Socket通信,同时支持UDP数据包通信,因而这里使用的是泛型实现的,在获得一个ServerSocketChannel对象之后,要注意将其从迭代器中删除,避免进行重复多次处理,接着执行第8行,创建一个IoSession对象出来,具体来讲我们这里创建的是NioSocketSession对象,调用的方法是accept,这个方法的第一个参数就是我们之前在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,默认情况下在他里面是会创建CPU个数+1个NioProcessor的,这个方法在
AbstractPollingIoAcceptor中是没有实现的,因此我们查看他的子类NioSocketAcceptor里面
NioSocketAcceptor$accept()
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);
}
这个方法里首先获得被选中ServerSocketChannel的key,接着对该key进行一系列的判断,接着第14行获取到和当前ServerSocketChannel有关联的SocketChannel,这里需要补充一点的就是ServerSocketChannel和Selector是通过SelectionKey来发生关联的,SelectionKey标志了我们当前ServerSocketChannel的状态,而如果说某一客户端想要和服务器某一端口服务发生关联的话,那么它实际上是和与该端口绑定的ServerSocketChannel发生联系的,因此我们就可以通过ServerSocketChannel获取与他有关联了客户端SocketChannel啦;最后执行第20行创建一个NioSocketSession对象,我们来看看他的构造函数;
NioSocketSession$NioSocketSession()
public NioSocketSession(IoService service, IoProcessor<NioSession> processor, SocketChannel channel) {
super(processor, service, channel);
config = new SessionConfigImpl();
this.config.setAll(service.getSessionConfig());
}
首先执行的是super的构造函数,其实就是NioSession的构造函数了,我们来看看
protected NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel) {
super(service);
this.channel = channel;
this.processor = processor;
filterChain = new DefaultIoFilterChain(this);
}
首先执行super的构造函数,实际上执行的是AbstractIoSession的构造函数,里面没有做多少事,我们不再展开讲,接着第5行创建了一个DefaultIoFilterChain对象出来,这个还是比较重要的,我们来看下里面做了什么事;
DefaultIoFilterChain$DefaultIoFilterChain()
public DefaultIoFilterChain(AbstractIoSession session) {
if (session == null) {
throw new IllegalArgumentException("session");
}
this.session = session;
head = new EntryImpl(null, null, "head", new HeadFilter());
tail = new EntryImpl(head, null, "tail", new TailFilter());
head.nextEntry = tail;
}
这个构造函数中为我们创建了两个EntryImpl类型的对象,分别封装的是HeadFilter和TailFilter对象,这里有必要说下DefaultIoFilterChain的作用了,在我们创建Session的时候,会为Session创建一个Filter责任链出来,那么责任链主要是干什么的呢?主要进行进行我们二进制与真正对象之间的转换啦,因为我们都知道在网络中传输的只能是字节,并不能传递对象,那么我们就需要字节和对象之间的转换,Filter链就是用来干这个的,当然你可以在客户端将要发送的数据通过Filter链来进行加密,在服务端再通过Filter链来进行解密,这个是完全可以的,既然是链嘛,就需要链头和链尾了;他们都会被封装到EntryImpl中,至于EntryImpl里面有什么我们就不贴出来了,主要就是prevEntry,nextEntry,nextFilter从名字上就能明显看出来主要是用于EntryImpl链拼接的实体罢了,有点类似于链表;
到此呢,我们的NioSocketSession就创建成功啦,创建NioSocketSession其实主要就是在它里面创建一个IoFilter责任链出来,用于处理当前Session的一些编解码工作,这样我们的NioSocketAcceptor的accept方法就执行结束了,返回了一个NioSocketSession对象,继续回到AbstractPollingIoAcceptor里面的processHandles方法,在第8行创建完NioSocketSession之后,执行第17行,将我们的NioSocketSession对象放到NioProcessor中,具体实现过程见下:
首先执行的是session的getProcessor方法,这里的session类型是NioSocketSession,所以我们去NioSocketSession里面查看getProcessor,你会发现它里面不存在这个方法,那就要去他的父类NioSession里面找了,果然我们找到了:
public IoProcessor<NioSession> getProcessor() {
return processor;
}
getProcessor里面的方法体非常简单,就是返回processor而已了,那么这个processor是在哪里赋值的呢?就是在创建NioSession的构造函数里面,我们在创建NioSocketSession的时候是会调用super来调用NioSession的构造函数的,也就是我们这里的processor就是我们在创建NioSocketAcceptor的时候创建的SimpleIoProcessorPool对象,接下来调用的就是它里面的add方法啦:
public final void add(S session) {
getProcessor(session).add(session);
}
可以看到在SimpleIoProcessor里面的add方法里,首先执行的是getProcessor,从SimpleIoProcessor里面获得一个Processor对象出来,具体来讲这里获得到的Processor类型将是NioProcessor类型,我们看看getProcessor方法
private IoProcessor<S> getProcessor(S session) {
IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);
if (processor == null) {
if (disposed || disposing) {
throw new IllegalStateException("A disposed processor cannot be accessed.");
}
processor = pool[Math.abs((int) session.getId()) % pool.length];
if (processor == null) {
throw new IllegalStateException("A disposed processor cannot be accessed.");
}
session.setAttributeIfAbsent(PROCESSOR, processor);
}
return processor;
}
这个方法最关键的就是第9行,获取到当前session的id,对其取绝对值,并且对我们创建SimpleIoProcessor的时候创建的NioProcessor数组进行取余运算,获得数组中的一个NioProcessor对象,默认情况下这个数组的大小是CPU个数+1;最后第15行将当前Session的PROCESSOR属性设置为获取到的NioProcessor;
那么到这里,实际上add操作执行的就是NioProcessor的add操作啦,我们查看NioProcessor里面会发现不存在这个方法,那么需要去他的父类AbstractPollingIoProcessor查看,代码见下:
AbstractPollingIoProcessor$add()
public final void add(S session) {
if (disposed || disposing) {
throw new IllegalStateException("Already disposed.");
}
// Adds the session to the newSession queue and starts the worker
newSessions.add(session);
startupProcessor();
}
将当前NioSocketSession添加到newSession里面,这里的newSessions实际上就是NioSocketSession队列,就是我们当前NioProcessor需要处理的NioSocketSession所组成的集合了,为什么还要这个集合呢?道理很简单嘛,刚刚你在通过getProcessor方法为NioSocketSession设置处理他的NioPrrocessor的时候,采用的方法是通过session的id对包含NioProcessor对象的数组进行取模运算的,这肯定就不能避免多个NioSocketSession同时都需要一个NioProcessor来处理的情况了,那么为了保存这些需要NioProcessor处理的NioSocketSession,自然需要一个队列来存储了;
紧接着执行了startupProcessor方法,如果你还记得上面的源码分析过程的话,会发现上面有调用过startupAcceptor方法,这两个方法不同之处在于一个是用于开启Processor线程执行它里面NioSocketSession请求的,一个是用于开启Acceptor来进行ServerSocketChannel的事件注册的,并且startupAcceptor只会执行一次,而startupProcessor会执行多次,默认情况下最多执行CPU个数+1次;
我们来看看startupProcessor方法:
AbstractPollingIoProcessor$startupProcessor
private void startupProcessor() {
Processor processor = processorRef.get();
if (processor == null) {
processor = new Processor();
if (processorRef.compareAndSet(null, processor)) {
executor.execute(new NamePreservingRunnable(processor, threadName));
}
}
// Just stop the select() and start it again, so that the processor
// can be activated immediately.
wakeup();
}
这个方法首先就是创建了一个Processor对象他实现了Runnable接口,随后调用executor的execute方法,将封装成NamePreservingRunnable的Processor放入线程池中,executor是CachedThreadPool类型的线程池,那么接下来就是执行Processor线程的run方法了:
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 (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {
// Last chance : the select() may have been
// interrupted because we have had an closed channel.
if (isBrokenConnection()) {
LOG.warn("Broken connection");
} 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();
}
}
// Manage newly created session first
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...
process();
}
// Write the pending requests
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()) {
boolean hasKeys = false;
for (Iterator<S> i = allSessions(); i.hasNext();) {
IoSession session = i.next();
if (session.isActive()) {
scheduleRemove((S)session);
hasKeys = true;
}
}
if (hasKeys) {
wakeup();
}
}
} catch (ClosedSelectorException cse) {
// If the selector has been closed, we can exit the loop
// But first, dump a stack trace
ExceptionMonitor.getInstance().exceptionCaught(cse);
break;
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
ExceptionMonitor.getInstance().exceptionCaught(e1);
}
}
}
try {
synchronized (disposalLock) {
if (disposing) {
doDispose();
}
}
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
} finally {
disposalFuture.setValue(true);
}
}
}
和Acceptor的run方法类似,同样存在一个死循环,第14行调用了Selector的select方法,但是和之前Acceptor中调用的select方法不同,我们这里调用的是有参数的select方法,这种方式会让我们的选择器每隔SELECT_TIMEOUT被唤醒一次,让他进行重新选择,目的就是为了管理空闲的NioSocketSession,而使用无参的select的话会一直阻塞下去,直到出现需要的事件为止;接着第43行执行了handleNewSessions方法
private int handleNewSessions() {
int addedSessions = 0;
for (S session = newSessions.poll(); session != null; session = newSessions.poll()) {
if (addNow(session)) {
// A new session has been created
addedSessions++;
}
}
return addedSessions;
}
可以看到通过for循环不停的poll出队列中存在的NioSocketSession对象,同时调用addNow方法对当前NioSocketSession中对应的SocketChannel进行OP_READ操作的注册,具体我们可以看看addNow方法:
AbstractPollingIoProcessor$addNow()
private boolean addNow(S session) {
boolean registered = false;
try {
init(session);
registered = true;
// Build the filter chain of this session.
IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
chainBuilder.buildFilterChain(session.getFilterChain());
// DefaultIoFilterChain.CONNECT_FUTURE is cleared inside here
// in AbstractIoFilterChain.fireSessionOpened().
// Propagate the SESSION_CREATED event up to the chain
IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners();
listeners.fireSessionCreated(session);
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
try {
destroy(session);
} catch (Exception e1) {
ExceptionMonitor.getInstance().exceptionCaught(e1);
} finally {
registered = false;
}
}
return registered;
}
这个方法首先第5行执行了init方法,这个方法就是用来为当前NioSocketSession对应的SocketChannel注册OP_READ事件的,具体实现是在NioProcessor里面的:
NioProcessor$init()
@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));
}
可以看到首先是获得当前NioSocketSession对应的SocketChannel对应,他是SelectableChannel的子类,接着将当前获得到的通道设置为非阻塞式,随后为其注册OP_READ事件;
这样的话,addNow方法执行结束了,由于这篇篇幅已经比较长了,所以决定在下一篇继续分析,未完,请继续查看下一篇;