源码面前,了无秘密。继之前阅读了Prototype、Spring、Tomcat、以及JDK的部分、Digester等等源码之后,学习一门技术,了解源码成了必备流程。也深深的感受到了源码面前,了无秘密的含义,同时也体会到它给我带来的好处。同时,也希望所有开发者,不论前端后端,如果有时间的话,都尽量看看源码吧。
接下来进入正题,这里要对Mina流程做一个分析。因为是指对NIO流程做了分析,所以这里说的也是NIO的执行流程。
先看一下Mina中主要类的大致结构:
接下来看看Mina的整个生命周期:
1、NioSocketAdaptor初始化分析
初始化时,设置相关依赖,例如:listeners, sessionconfig,handler, executor等,这个过程没什么可说的。
2、启动并接收请求
启动的入口是bind(SocketAddress),先来看看这部分的序列图:
这一阶段,
2.1 将ServerSocketChannel交由Selector来管理。对应序列图中的regiesterHandlers()操作。
2.2 接收SocketChanel
1)使用ServerSocketChannel接收SocketChannel。
2)将接收到的SocketChannel封装成IoSession,
3)添加到newSessions队列中。
此外,IoSession会以attachment的方式捆绑到与SocketChannel关联的SelectionKey中。这样做的目的是在进行Selector.select()之后,可以根据key直接拿到IoSession。
对应于序列图中的processHandler()操作。
3、处理请求分析
处理请求是由IoProcessor完成的,先来看看这部分的序列图:
这一阶段:
3.1 调用selector.select(),查看是否有可处理的SocketChannel
如果没有,就结束本轮处理。如果有,才进行后续操作。
3.2处理最新添加的session,方法是handlernewsessions()。过程是:
1) 从newSessions队列中取出IoSession,
2) 设置为非阻塞,并以 READ方式注册到Selector中,这才由Selector来接管这些SocketChannel。
3)为IoSession创建FilterChain
4)将Session放到managedSession集合中。
5)触发Session创建完毕事件。
3.3 交通管制处理。
本过程对应操作是updateTrafficMask()。
有时,我们需要在程序中控制是否进行写读操作,或者暂停读操作。要实现这个功能,可以使用IoSession的resumeRead()或者supendRead()。然而,程序中虽然调用了这两种方法,但是要想真正的对IO进行控制,还得通过注册感兴趣的操作来完成。这一点程序中并没有完成。
这一过程中,其实就是完成帮助应用程序完成未完成的操作。
3.4 根据兴趣事件进行相应处理。
这一过程对应序列图中的process()操作。
1) 拿到所有的通过selector.select(TIME_OUT)筛选出来的可以处理的SelectionKey
2) 根据SelectionKey,取到与之捆绑的IoSession。
3) 处理感兴趣的事件。
如果对读感兴趣,进入读取数据、业务处理流程。从序列图中,可以看出从SocketChannel读取数据,然后经过FilterChain,最后到达IoHandler。
如果对写感兴趣,进入属性数据处理流程。对于所有的对写感兴趣的IoSession,然后放到flushingSessions队列中。
4)对每一个Session处理完兴趣操作后,都有从3.1选择到的Set<SelectionKey>中移除关联的SelectionKey。
3.5 处理对写事件感兴趣的Session。
这一过程对应的是序列图中的flush()操作。
如果应用程序中使用了session.write()操作,例如示例程序。该session就会被放到flushingSessions队列中。
该过程操作是:
1) 就是将flushingSessions队列中的session取出来
2) 清除写兴趣
3) 处理session中的写请求队列(writeRequestQueue),也就是将writeRequestQueue中的每一个写请求中数据写到SocketChannel中。
4) 如果没有写完毕,还有剩余,就再次注册写兴趣。
5) 如果没有写完毕,就再放到flushingSeesions队列中。
3.6 清理需要移除的Session
这个过程对应序列图中的removeSessions()。
在3.2到3.5过程中,如果哪个过程出现了Exception,或者与客户端的连接断开了等情况下,相关联的Session都会被放到removingSessions队列中。
这个过程的处理是:
1)从removingSessions队列中取出每一个Session
2)如果Session处于处理阶段,清理writeRequestQueue,调用SelectionKey.cancel()来取消Selector对它的管理。
3)如果Session还处于未处理阶段,也就是也存在于newSessions队列中,就从newSessions队列移除之。
3.7 做空闲处理
对应于序列图中的notifyIdleSessions()操作。