mina架构分析
使用的版本号是2.0.9
IoService分析

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);//这个future是一个用于线程间异步通信结果的类。它能够不被中断的等待异步操作的结果
        // 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();//这里启动acceptor线程
        // 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();//lock是一个semaphone,用于同步acceptor线程,保证该线程在成功创建并開始执行后。再执行兴许的代码
            // 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();//这里异步等待acceptor线程设置结果。并导致当前线程被唤醒
        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;
    }
    /**
     * This method is called by the doBind() and doUnbind()
     * methods.  If the acceptor is null, the acceptor object will
     * be created and kicked off by the executor.  If the acceptor
     * object is null, probably already created and this class
     * is now working, then nothing will happen and the method
     * will just return.
     */
    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();//创建Acceptor实例
            if (acceptorRef.compareAndSet(null, acceptor)) {
                executeWorker(acceptor);//启动acceptor线程
            } else {
                lock.release();
            }
        }
    }
public void run() {
assert (acceptorRef.get() == this);
int nHandles = 0;
// Release the lock
lock.release();//进程開始执行了,释放lock
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();//调用select接口,派生类须要实现之
// this actually sets the selector to OP_ACCEPT,
// and binds to the port on which this class will
// listen on
nHandles += registerHandles();//遍历registerQueue中的绑定请求,并调用open接口。实际上就是由派生类实现bind地址的功能。
// 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());//调用accept函数接收用户请求,创建Session,并把session增加到processor中
                    }
                    // 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();
                    }
                }
            }
        }
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VuYnhvbmxpbmU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
.png)
为了深入理解Session。我们须要了解例如以下几个问题:
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);
}
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);
}
}
。
。
。
        // Now, we can write the message. First, create a future
        WriteFuture writeFuture = new DefaultWriteFuture(this);
        WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);
        // Then, get the chain and inject the WriteRequest into it
        IoFilterChain filterChain = getFilterChain();
        filterChain.fireFilterWrite(writeRequest);
         。。。
        // Return the WriteFuture.
        return writeFuture;
    }
它的fireFilterWrite方法实际上是,从tail到head遍历链表,既然是反向遍历。那么Head是最后一个被遍历到的filter。这个head是一个HeadFilter实例
@SuppressWarnings("unchecked")
@Override
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
AbstractIoSession s = (AbstractIoSession) session;
// Maintain counters.
if (writeRequest.getMessage() instanceof IoBuffer) {
IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
// I/O processor implementation will call buffer.reset()
// it after the write operation is finished, because
// the buffer will be specified with messageSent event.
buffer.mark();
int remaining = buffer.remaining();
if (remaining > 0) {
s.increaseScheduledWriteBytes(remaining);
}
} else {
s.increaseScheduledWriteMessages();
}
WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();//获取session中的写请求队列
if (!s.isWriteSuspended()) {
if (writeRequestQueue.isEmpty(session)) {
// We can write directly the message
s.getProcessor().write(s, writeRequest);//使用processor写writeRequest,实际上还是把writeRequest写入到写请求队列中了
} else {
s.getWriteRequestQueue().offer(s, writeRequest);
s.getProcessor().flush(s);
}
} else {
s.getWriteRequestQueue().offer(s, writeRequest);//把writeRequest写入到写请求队列中
}
}
@SuppressWarnings("unchecked")
@Override
public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
((AbstractIoSession) session).getProcessor().remove(session);
}
}
那么问题来了。这个写队列是从哪冒出来的,它是怎样创建,又是谁从这个队列中把写请求取出来,发送出去的呢?
protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) {
。。
。
// Every property but attributeMap should be set now.
        // Now initialize the attributeMap.  The reason why we initialize
        // the attributeMap at last is to make sure all session properties
        // such as remoteAddress are provided to IoSessionDataStructureFactory.
        try {
            ((AbstractIoSession) session).setAttributeMap(session.getService().getSessionDataStructureFactory()
                    .getAttributeMap(session));
        } catch (IoSessionInitializationException e) {
            throw e;
        } catch (Exception e) {
            throw new IoSessionInitializationException("Failed to initialize an attributeMap.", e);
        }
        try {
            ((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()
                    .getWriteRequestQueue(session));
        } catch (IoSessionInitializationException e) {
            throw e;
        } catch (Exception e) {
            throw new IoSessionInitializationException("Failed to initialize a writeRequestQueue.", e);
        }
        if ((future != null) && (future instanceof ConnectFuture)) {
            // DefaultIoFilterChain will notify the future. (We support ConnectFuture only for now).
            session.setAttribute(DefaultIoFilterChain.SESSION_CREATED_FUTURE, future);
        }
        if (sessionInitializer != null) {
            sessionInitializer.initializeSession(session, future);
        }
        finishSessionInitialization0(session, future);
    }
好吧,我们还得接着寻找。session.getService是谁呢?这里的session显然是NioSession,service是NioSocketAcceptor,getSessionDataStructureFactory方法是在基类AbstractIoService中定义的。在默认情况下。get出来的实例。是DefaultIoSessionDataStructureFactory的类实例。我们再来看这个类
public IoSessionAttributeMap getAttributeMap(IoSession session) throws Exception {
return new DefaultIoSessionAttributeMap();
}
public WriteRequestQueue getWriteRequestQueue(IoSession session) throws Exception {
return new DefaultWriteRequestQueue();
}
private static class DefaultIoSessionAttributeMap implements IoSessionAttributeMap {
private final ConcurrentHashMap<Object, Object> attributes = new ConcurrentHashMap<Object, Object>(4);
/** A queue to store incoming write requests */
private final Queue<WriteRequest> q = new ConcurrentLinkedQueue<WriteRequest>();
。。
WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();
writeRequestQueue.offer(session, writeRequest);
if (!session.isWriteSuspended()) {
this.flush(session);
}
}
.getAttributeMap(session));
.png)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VuYnhvbmxpbmU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
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);//select事件,看是否有读写,关闭事件发生
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
nSessions += handleNewSessions();//这里会处理newSessions,就是在acceptor线程中add进来的,基本上来说。就是创建filterChain,并触发sessionCreated事件和sessionOpen事件。
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();//这里会对发生了事件的session进行处理。假设session是读事件,会调用session底层的channel去读数据,并触发session的messageReceived时间,假设session是写事件。会把session增加到flushingSessions队列里
}
// Write the pending requests
long currentTime = System.currentTimeMillis();
flush(currentTime);//这里会处理flushingSessions队列。调用session底层的channel去发送数据
// 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
// 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);
}
}
}
这些processor线程是用来检查是否有读写事件的。
用户加入到filterChain的filter都是在这个线程中运行的,最后会把事件传递给handler进行终于的处理。也就是说。当有多个session的时候,会有多个processor线程,session的个数是大于等于processor的个数的。
同一时候,一个processor会相应多个session,单一个session仅仅相应一个processor线程。
mina架构分析的更多相关文章
- Mina架构与优化指南
		
MINA架构 这里,我借用了一张Trustin Lee在Asia 2006的ppt里面的图片来介绍MINA的架构. Remote Peer就是客户端,而下方的框是MINA的主要结构,各个框之间的箭头代 ...
 - SLG手游Java服务器的设计与开发——架构分析
		
微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...
 - tomcat架构分析 (Session管理)
		
Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到.在开发app时,我们只是获取一个session,然后向session中存取数据,然后再销毁session.那么如何产生se ...
 - Magento架构分析,Magento MVC 设计分析
		
Magento架构分析,Magento MVC 设计分析 分类:Magento 标签:Magento MVC.Magento架构 669人浏览 Magento 采用类似 JAVA的架构,其扩展与稳定性 ...
 - Flickr 网站架构分析
		
Flickr 网站架构分析 Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被 ...
 - Android架构分析之Android消息处理机制(二)
		
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们 ...
 - NopCommerce架构分析(转载)
		
原文 一,NopCommerce架构分析之开篇 NopCommerce是.net开源项目中比较成熟的一款业务应用框架,也是电子商务系统中的典范.所以很想多学习一下里面的设计和实现方式. 二,NopCo ...
 - Qualcomm Android display架构分析
		
Android display架构分析(一) http://blog.csdn.net/BonderWu/archive/2010/08/12/5805961.aspx http://hi.baidu ...
 - tomcat架构分析-索引
		
出处:http://gearever.iteye.com tomcat架构分析 (概览) tomcat架构分析 (容器类) tomcat架构分析 (valve机制) tomcat架构分析 (valve ...
 
随机推荐
- VS Code 终端显示问题
			
一.打开编辑器的终端时候,然后弹出了系统自带的cmd窗口 解决办法: Win+R 输入cmd 打开windows cmd窗口,窗口顶部右键属性,然后取消勾选使用旧版控制台,然后重启编辑器就行了. 二. ...
 - AtCoderACGC001C Shorten Diameter
			
Description: 给定一个\(n\)个点的树,要求删去最少的点使树的致直径不超过k Solution: 如果\(k\)为偶数,对于最终状态一定是以每一个点为根任何点的深度不能超过\(k/2\) ...
 - 谈一谈Nginx的强大
			
什么是Nginx? Nginx是一款高性能,开源的,支持高并发而轻量级的Web服务器,同时也是具有反向代理服务器及电子邮件(IMAP/POP3)的代理服务器. 基于REST架构风格,并且以统一资源描述 ...
 - 【iOS与EV3混合机器人编程系列之中的一个】iOS要干嘛?EV3能够更酷!
			
乐高Mindstorm EV3智能机器人(下面简称EV3)自从在2013年的CES(Consumer Electronics Show美国消费电子展)上展出之后,就吸引了全球广大机器人爱好者的眼球!E ...
 - 二、Docker基础操作
			
原文:二.Docker基础操作 一.下载镜像 命令:docker pull xxxxxx(镜像名) docker pull training/weapp 二.运行镜像 docker run -d -P ...
 - 今天发现里一个非常好用的Listbox自绘类,带不同文字字体和图片,觉得很有必要记下来
			
代码简写 MyListBox.h class CUseListBox : public CListBox { typedef struct _ListBox_Data { CString strApp ...
 - Dubbo学习总结(4)——Dubbo基于Zookeeper实现分布式实例
			
入门实例解析 第一:provider-提供服务和相应的接口 创建DemoService接口 [java] view plaincopyprint? <span style="font- ...
 - MySQL 使用mysqld_multi部署单机多实例详细过程
			
,如何使用多实例 由于多实例中,各个实例的资源都是不share的,所以要合理分配好各个实例的内存.磁盘等资源,避免out of memery或则 full disk的情况出现.
 - hdu 2795 Billboard(线段树单点更新)
			
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
 - imageView-scaleType 图片压缩属性
			
今天用到了图片压缩的属性,自己参照网上的说明,验证了一下,截图如下 (1)当图片背景是方形的时候 代码如下 <LinearLayout android:id="@+id/l31&quo ...