深入阅读Mina源码(1) —— 小试牛刀,过滤器介绍

(ps:写完后觉着第一博还是应该说一下Mina的简介,就附上了0部分吧)

 

0. Mina框架简介 

 

    MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架。通过使用MINA框架可以可以省下处理底层I/O和线程并发等复杂工作,开发人员能够把更多的精力投入到业务设计和开发当中。MINA框架的应用比较广泛,应用的开源项目有Apache Directory、AsyncWeb、Apache Qpid、QuickFIX/J、Openfire、SubEthaSTMP、red5等。

    MINA框架的特点有:基于java NIO类库开发;采用非阻塞方式的异步传输;事件驱动;支持批量数据传输;支持TCP、UDP协议;控制反转的设计模式(支持Spring);采用优雅的松耦合架构;可灵活的加载过滤器机制;单元测试更容易实现;可自定义线程的数量,以提高运行于多处理器上的性能;采用回调的方式完成调用,线程的使用更容易。 

 

1. 引言

 

    近期的项目需要用到Mina,比较粗浅的在上层简单的搭建自己的项目,但是有种非常疑惑并且没有安全感的想法,就决定看下Mina的源码,并且网络部分的知识还是很重要的。第一次写源码分析的博客,也没有经验,不知道怎么说好,是不是需要细致摄入。我就避开入门和介绍部分,直接分析源码了,可能比较细微一点,也留作自己日后查看。(ps:如果要找Mina的介绍、入门相关的博客在iteye一搜就会有很多相关的介绍的,可以先看一下整体框架和基本的使用方法,方便理解)

    直接看源码可能会有找不到头绪的事情,站在巨人的肩膀上还是很好的方法,我选择了这个博客,先给出作者的地址,我会首先按照作者的思路分成四部分介绍,如果需要补充在增加文章,还是很推荐这几篇文章,有些作者说的清楚的我就不再重复描述,但是对于有些说的不清楚,或UML图有问题的我会说明一下,做少量的引用。


    http://wslfh2005.iteye.com/ 作者有四篇文章,不再详细列出,


    如果查看了Mina的入门博客,或者官方文档肯定对于类似下面这个图比较熟悉了,可以很清楚的看到IoFileterChain的过滤层,Mina对三层进行了很好的分离,使得连接维护,信息的预处理,逻辑处理很好的分离。我们首先来单个模块剥离,按照推荐的博客的顺序首先介绍过滤器层。

深入阅读Mina源码(1) —— 小试牛刀,过滤器介绍_第1张图片

 

 

2.Mina过滤器机制的实现

 

 

 Mina的核心代码都在core中,包结构也很清晰,此部分代码都在core.filterchain中,目录下有一下几个类:

 

深入阅读Mina源码(1) —— 小试牛刀,过滤器介绍_第2张图片   

 

最重要的基础类和接口是:IoFilter、IoFilterChain、DefaultIoFilterChain。

这个图和引用的博客的图都不能很好的看出来这些文件的直接关系,下面就先看一下,有个直观认识

 

深入阅读Mina源码(1) —— 小试牛刀,过滤器介绍_第3张图片

 

2.1 IoFilter

 

    看一下IoFilter定义的方法,可以把IoFilter简单的分成两部分。

    一部分是跟IoFilterChain相关的,在添加、删除相应的Filter之后调用,如:

 

 

/**
     * Invoked before this filter is added to the specified <tt>parent</tt>.
     * Please note that this method can be invoked more than once if
     * this filter is added to more than one parents.  This method is not
     * invoked before {@link #init()} is invoked.
     *
     * @param parent the parent who called this method
     * @param name the name assigned to this filter
     * @param nextFilter the {@link NextFilter} for this filter.  You can reuse
     *                   this object until this filter is removed from the chain.
     */
    void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception;
 

    另一部分是跟消息处理相关的,如:

 

 

/**
     * Filters {@link IoHandler#messageReceived(IoSession,Object)}
     * event.
     */
    void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception;
 

    其他就是跟初始化、销毁等相关的操作了。


    可以看到IoFilter的实现类IoFilterAdapter中其实没有过多的实现其中细致的操作,在sessionCreated等session相关和Message相关的方法中相应的调用了nextFilter的对应方法完成消息的传递,作为父类,可以看到消息传递的影子了。

    这里最值得关注的部分在于IoFilter中其中定义了一个NextFilter接口,NextFilter包含了IoFilter中提到的两部分全部方法,没有包含最后提到的初始化、销毁等相关的代码,而且最奇怪的就是在IoFilterAdapter没有实现这个接口。观察这个包的类图和命名,可以想象的到在维护消息连的DefaultIoFilterChain中肯定实现了此接口,通过找到EntryImpl的构造函数中为nextFilter赋值的时候实现了此接口,先避开细节只看nextFilter

 

 

public void sessionCreated(IoSession session) {
        Entry nextEntry = EntryImpl.this.nextEntry;
        callNextSessionCreated(nextEntry, session);
}

private void callNextSessionCreated(Entry entry, IoSession session) {
        try {
            IoFilter filter = entry.getFilter();
            NextFilter nextFilter = entry.getNextFilter();
            filter.sessionCreated(nextFilter, session);
        } catch (Throwable e) {
            fireExceptionCaught(e);
        }
}

 

    这段代码直接反应了所有方法的实现,都是通过这一种方式实现了转发的作用,这也是整个NextFilter方法的作用,具体的业务逻辑依然在Filter中执行,相同的名字只是为了维持含义的一致性,NextFilter的实例则维护在EntryImpl。下面就进入IoFilterChain看一下这个类。

 

2.2 IoFilterChain

 

    下面就按照最直观的名字先来看IoFilterChain,接口用来构造一个容器管理所有的IoFilter,里面更细节的定义了一个Entry接口,来表示每个FilterChain中的节点,Entry定义的方法如下

 

深入阅读Mina源码(1) —— 小试牛刀,过滤器介绍_第4张图片


    现有个直观的认识,可以看到entry内容非常少,作用就是完成了维护前后和本身的IoFilter的工作,使得所有的Entry通过自身中的记录组成了一条链。

通过之前的图可以看到DefaultIoFilterChain是Mina给出的IoFilterChain默认实现类,因此必然有Entry的实现,找到发现是EntryImpl,EntryImpl构造函数为:

    private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) 

    通过这个构造函数就可以看出是如何实现前面的接口的了,只是为什么需要preEntry呢,只是为了传入Filter吗,如果答案是是的话Mina肯定不会这么做的。这里又一次要看NextFilter这个接口的实现了,可以看到filterWrite和filterClose方法都是逆向传递的:

 

 

public void filterWrite(IoSession session, WriteRequest writeRequest) {
        Entry nextEntry = EntryImpl.this.prevEntry;
        callPreviousFilterWrite(nextEntry, session, writeRequest);
}

public void filterClose(IoSession session) {
        Entry nextEntry = EntryImpl.this.prevEntry;
        callPreviousFilterClose(nextEntry, session);
}

 


    这里就看到了为什么Mina要大费干戈,而不采用简单的方式实现,和这里NextFilter接口设计的精妙之处,第一张架构图中可以看到消息传递的双向性,不管是服务器和客户端,消息的接受和发送总是反方向进行的,起点也不同。而通过实现nextFilter接口其实就很好的封装了消息传递的顺序,仅仅在Entry一层去控制所有方法向上还是向下传递,而对于外部的消息连而言只需要调用跟Filter同名的方法即可,虽然这里看到了调用的Chain中带有Previous和Next的方法,但是实际上内部并没有做相应的行为,都是只简单的获取nextFilter,因为nextFilter中的方法已经定义了相应的向上和向下传递。至于为什么叫这个名字应该是为了很好的表明这些方法的顺序和含义,另外nextFilter中调用的Chain中的方法全部都是private,因此对子类理解和修改没有影响。(刚才代码中调用的callPreviousFilterWrite如下)

 

 

private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {
        try {
            IoFilter filter = entry.getFilter();
            NextFilter nextFilter = entry.getNextFilter();
            filter.filterWrite(nextFilter, session, writeRequest);
        } catch (Throwable e) {
            writeRequest.getFuture().setException(e);
            fireExceptionCaught(e);
        }
}

 

    前面主要说的entry和nextfilter,接下来就单纯的针对Chain来看一下做了什么事情。DefaultIoFilterChain中维护了一个header和tail,很容易想到的就是链表,刚才提到entry通过内部的维持其实很方便来实现这样的链表。因此DefaultIoFilterChain的作用就是维护链表、提供nextFilter调用的filter相关方法完成传递,最后TailFilter将消息传递给IOHandler处理。包括IoFilterChain接口中除了fire方法也只是定义了很多关于链表的操作(fire方法后面再说)。

 

2.3 IoFilterEvent

 

    该类继承IoEvent,完成基于事件的处理模型,当一个事件(比如接收到消息)发生后会触发相应的事件,进而调用过滤器链的消息处理功能进行消息的处理和转发。这也是下一节要说的,提到这个类,因为刚才漏下了IoFilterChain的一系列fire方法没有介绍,如果不说显得不太完整了,IoFilterEvent基本只为了一个fire方法,就是根据构造方法中传入的IoEvent事件的类型区调用相应的nextFilter的方法,这里只是为子类提供一种事件处理的机制,搜索一下此类,可以看到在实现具体的filter时有些地方用到了,去更好的完成Filter或基于事件的流程控制(core之外的包看的少,不多误导)。另外从IoFilterEvent中可以看到这个利用NextFilter时已经不可见消息传递的方向了。根据事件去传递,用chain中提供的fire系列方法也可以同样完成。这个类代码很少,就不再粘贴和介绍。

 

2.4 DefaultIoFilterChainBuilder和IoFilterChainBuilder

 

    接口就是builder机制,不多说。细说一下实现类,它是个builder机制,但是它完成了很多IoFIlterChain需要完成的工作,它直接利用并发包中的CopyOnWriteArrayList来提供一个chain的构造操作,并且尚未构建IoFIlterChain之前,是可以修改list中的filter的,整个list的维护都对用户可见。builder实现了自己的entry,但是相对来说很简单,没有再每个entry维持链接关系。


    继承自接口的主要是build方法:

 

 

public void buildFilterChain(IoFilterChain chain) throws Exception {
        for (Entry e : entries) {
            chain.addLast(e.getName(), e.getFilter());
        }
}

 

 

    只是简单地把entry交进去,那链接关系是怎么创建的呢。感兴趣可以看一下DefaultIoFilterChain的addLast,其中检查了可插入行后就调用了register方法。

 

 

private void register(EntryImpl prevEntry, String name, IoFilter filter) {
        EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);

        try {
            filter.onPreAdd(this, name, newEntry.getNextFilter());
        } catch (Exception e) {
            throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + filter + " in " + getSession(), e);
        }

        prevEntry.nextEntry.prevEntry = newEntry;
        prevEntry.nextEntry = newEntry;
        name2entry.put(name, newEntry);

        try {
            filter.onPostAdd(this, name, newEntry.getNextFilter());
        } catch (Exception e) {
            deregister0(newEntry);
            throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + filter + " in " + getSession(), e);
        }
}

 

    register方法又把有用的参数构造了新的DefaultIoFilterChain自己实现的EntryImpl, 肯定记得这样就会产生新的nextFilter,来调用entry中存储的前后节点,从而完成了chain的组成。

 

 

2.5 IoFilterLifeCycleException

 

    最后exception只是继承了RuntimeException,没有做其他的事情,用来标明是infilter中onPostAdd、onPreAdd等在加入Chain之前的操作发生异常。

 

 

2.6 Conclusion

 

    此部分主要是介绍了core中的消息链实现的机制,下面还要继续深入阅读其他部分源码,继续分享。因为是按照单独的模块阅读的代码,可能存在一些短见和没认识到的机制。最后看完所有的之后再做一次整合的介绍,和一个Mina上层的demo案例。今天到这了,碎觉。

你可能感兴趣的:(java,socket,Mina,过滤器)