Mina 源码阅读:Server端基于NIO的处理流程

jopen 9年前
 

源码面前,了无秘密。继之前阅读了Prototype、Spring、Tomcat、以及JDK的部分、Digester等等源码之后,学习一门 技术,了解源码成了必备流程。也深深的感受到了源码面前,了无秘密的含义,同时也体会到它给我带来的好处。同时,也希望所有开发者,不论前端后端,如果有 时间的话,都尽量看看源码吧。

接下来进入正题,这里要对Mina流程做一个分析。因为是指对NIO流程做了分析,所以这里说的也是NIO的执行流程。

先看一下Mina中主要类的大致结构:

Mina 源码阅读:Server端基于NIO的处理流程

接下来看看Mina的整个生命周期:

1、NioSocketAdaptor初始化分析

初始化时,设置相关依赖,例如:listeners, sessionconfig,handler, executor等,这个过程没什么可说的。

2、启动并接收请求

启动的入口是bind(SocketAddress),先来看看这部分的序列图:

Mina 源码阅读:Server端基于NIO的处理流程

这一阶段,

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完成的,先来看看这部分的序列图:

Mina 源码阅读:Server端基于NIO的处理流程

这一阶段:

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()操作。