`
384444165
  • 浏览: 258781 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

深入阅读Mina源码(3) —— Mina之IOAdapter(一)

阅读更多

1. 预览

 

     依然遵循之前的原则,只看core(其实还包括了transport,虽然它不在core中,但socket相关的类在此包中),了解mina的内部机制,忽略细节,这里就直接从服务器的入口第一步开始,即连接管理的IOService。直接来看下对应的包:org.apache.mina.core.service。 by the way 我的mina源码版本是:2.0.7

 

 

 

     通过包结构就可以说一下这部分的职能和相关的代码,先有个整个的印象。

     可以总结成五部分:service责任(IoService)、Processor线程处理(IoProcessor)、handler处理器(IoHandler)、服务器端接收器(IoAcceptor)和客户端连接器(IoConnector),括号中是对应的处理接口,我们首先只讨论服务器架构图入口部分,即1、4、5。另外handler部分属于流程的末端,IoProcessor和IoSession密切相连,负责完成IoSession中一些实际的IO操作,不太适合在这部分介绍。

 

 

2. IOService

 

      第一部分先说一下顶层的接口IOService,官方的介绍上给出了IOService的职能和abstract封装了一层的抽象类

 

 

    对应着这几个功能看一下IoService的所有方法就比较清楚各个方法的作用了。因为是接口,看了这个接口大概就明白是做什么的了。

    AbstractIoService的注释提到An instance of IoService contains an Executor which will handle the incoming events。而上面提到的不止这个功能,照着上面的功能去看相应的方法是个比较好的办法,就可以了解每部分怎么实现的,一眼看过去有些add,set,contain之类的方法都可以忽略了,这里最后的manages不要被字面误解,搜一下源码里的管理,其实只是maintain。那么就剩下了两个方法值得我们关注,一个就是注释提到的executor,另一个就是涉及到默认实现的dispose。

 

    Creates Executor, if not provided(在构造方法中实现,觉着有些地方同样需要提一下跟整体的设计相关的,就增大的信息量,把构造方法都贴出来了):

 

 

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();
}
 

 

        只是看一下上面提到的功能,这里多提一下就是 ExceptionMonitor.getInstance(); 有兴趣去看一下org.apache.mina.uti.DefaultExceptionMonitor 很简短,只有一个方法,这里只调用一下就是实例化DefaultExceptionMonitor :

 

 

public void exceptionCaught(Throwable cause) {
        if (cause instanceof Error) {
            throw (Error) cause;
        }

        LOGGER.warn("Unexpected exception.", cause);
}
 

 

      我想大概明白是怎么一回事了,这里对Exception的处理做了初始化,这个位置也比较合适的选择,Mina将异常处理的日志打印放在了单独的类中进行处理uncaught exceptions。

 

      回到之前的话题,看到mina是通过线程池来装载这些服务的,如果你像我一样发现自己弱爆了concurrent包没用过就到网上看看吧,在学习过程中大致熟悉了之后再发相关的博客,现在发肯定要误导大众了。

      这里只需要知道下面这句话就ok了:并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。

 

      下面来看dispose方法,这段代码算是学习并发包一个很好的例子了:

 

 

protected final Object disposalLock = new Object();
private volatile boolean disposing;
private volatile boolean disposed;
public final void dispose(boolean awaitTermination) {
        if (disposed) {
            return;
        }

        synchronized (disposalLock) {
            if (!disposing) {
                disposing = true;

                try {
                    dispose0();
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);
                }
            }
        }

        if (createdExecutor) {
            ExecutorService e = (ExecutorService) executor;
            e.shutdownNow();
            if (awaitTermination) {

                //Thread.currentThread().setName();

                try {
                    LOGGER.debug("awaitTermination on {} called by thread=[{}]", this, Thread.currentThread().getName());
                    e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                    LOGGER.debug("awaitTermination on {} finished", this);
                } catch (InterruptedException e1) {
                    LOGGER.warn("awaitTermination on [{}] was interrupted", this);
                    // Restore the interrupted status
                    Thread.currentThread().interrupt();
                }
            }
        }
        disposed = true;
}
 

 

    dispose的作用在官网的IOAdapter中明确提到了,还是尽量使用官方的解释:

 

 

Disposal

The service can be stopped by calling the dispose() method. The service will be stopped only when all the pending sessions have been processed :

// Stop the service, waiting for the pending sessions to be inactive
acceptor.dispose();

You can also wait for every thread being executed to be properly completed by passing a boolean parameter to this method :

// Stop the service, waiting for the processing session to be properly completed
acceptor.dispose( true );

 

 

      那顺着它的作用看下源码,仅仅是内容大都比较好理解,主要是学习下实现的机制,这里用了volatile关键字修饰布尔型变量的方法,同时使用内置锁机制控制线程的访问。

     dispose0用于子类实现,来释放相应的资源,createdExecutor在刚才看到的构造函数中初始化,对应是否做了Executors.newCachedThreadPool()开启线程池。

     推荐一篇volatile的文章:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html,使用需谨慎,多学习优秀做法。

 

 

3. IOAdapter

 

 

    We have many of those implementing classes

  • NioSocketAcceptor : the non-blocking Socket transport IoAcceptor
  • NioDatagramAcceptor : the non-blocking UDP transport IoAcceptor
  • AprSocketAcceptor : the blocking Socket transport IoAcceptor, based on APR
  • VmPipeSocketAcceptor : the in-VM IoAcceptor

 

    又来张图片,木有办法,mina官网的资料就是那么给力了,因此User Guide还是必备资料。这里还是要复制大多数入门篇都在写的那段官方Quick start guide的服务器代码了:

 

 

public class MinaTimeServer
{
    private static final int PORT = 9123;

    public static void main( String[] args ) throws IOException
    {
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
        acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));

        acceptor.setHandler( new TimeServerHandler() );
        acceptor.getSessionConfig().setReadBufferSize( 2048 );
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
        acceptor.bind( new InetSocketAddress(PORT) );
    }
}
 

 

 

     先走上面的图左边走下去,NioSocketAcceptor应该是最常用到的,Mina构造socket服务器也是近期项目需要用到的。

     首先IoAcceptor在IoService基础之上又实现了一些自己定义的方法,主要是bind系列和SocketAddress相关系列,看到要到NioSocketAcceptor左右都需要继续向下一层。继续粘图就太多了,这里可以对应一下类的方法列表看,SocketAdaptor出现了分叉,也就是和DatagramAcceptor有区分的定义了一些新的方法,但是方法很少,先不详细说明。同时比如getDefaultLocalAddress方法的返回值缩小了范围,变成了SocketAddress的子类InetSocketAddress,同时这部分的代码都已经在了org.apache.mina.transport.socket包下了。

     左边已经都到的NioSocketAcceptor的直接实现接口,那么继续开始从右边向下,首先是继承自AbstractIoService的AbstractIoAcceptor,主要为了实现IoAcceptor通用的方法,就是刚才说到的bind系列和SocketAddress相关系列。先看一下定义的几个属性

 

 

    private final List<SocketAddress> defaultLocalAddresses = new ArrayList<SocketAddress>();

    private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections
            .unmodifiableList(defaultLocalAddresses);

    private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>();

    private boolean disconnectOnUnbind = true;

    /**
     * The lock object which is acquired while bind or unbind operation is performed.
     * Acquire this lock in your property setters which shouldn't be changed while
     * the service is bound.
     */
    protected final Object bindLock = new Object();
 

 

     疑问马上来了:

 

  • 为什么要用List形式的defaultLocalAddresses来维护本地的默认地址
  • bind的是地址维护用的Set,而defaultLocalAddresses用的List,这是为什么

     那就一个一个来回答吧:

 

  • 使用List来维持本地地址是考虑到了机器多网口的问题,可见Mina设计的细致。
  • 为什么两个要用不同的方式来维护呢,原因很简单。先看一下getDefaultLocalAddress返回的是return defaultLocalAddresses.iterator().next(); 而另个bound的地址,想一下unbind后会怎么操作了呢,再看一眼HashSet,相信凭直觉就知道了原因,mina可以用一个IoAdapter绑定多个端口进行监听,而更好的管理是必要的,也就是为什么使用了HashSet,而非List。

 

     这里提到了getDefaultLocalAddress方法就对应了另一个getLocalAddress方法,getLocalAddress返回的是boundAddresses拷贝的一份Set中的next,需要注意这一点,因为他们都是public方法。已经长篇大论了,这个细节的地方提示注意一下,就不贴代码了。

     另一个提一下就是unmodifiableList这个方法,简直就是刚看过的Effective java中的‘使类和成员的可访问性最小化’中讲解的完美实例,学习就是在这么一次次的突然新鲜东西和学习的东西碰撞中产生乐趣和不断熟悉的,突然有种两者都恍然大悟的感觉,扯远了。说一下这个方法是对list做一个不可变的拷贝,因为后面有提供返回list的方法提供,因此需要维护这样一个变量(因为长度非0的数组总是可变的,再深入就不是这里要讨论的了)。

     接下来就是这个AbstractIoAcceptor类的核心两个方法了,其他一切方法都是围绕他们进行的:bind和setDefaultLocalAddresses,本想都拿代码出来说一下的,但是太多了,就说一下setDefaultLocalAddresses

 

 

public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
        if (localAddresses == null) {
            throw new IllegalArgumentException("localAddresses");
        }

        synchronized (bindLock) {
            synchronized (boundAddresses) {
                if (!boundAddresses.isEmpty()) {
                    throw new IllegalStateException("localAddress can't be set while the acceptor is bound.");
                }

                Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>();

                for (SocketAddress a : localAddresses) {
                    checkAddressType(a);
                    newLocalAddresses.add(a);
                }

                if (newLocalAddresses.isEmpty()) {
                    throw new IllegalArgumentException("empty localAddresses");
                }

                this.defaultLocalAddresses.clear();
                this.defaultLocalAddresses.addAll(newLocalAddresses);
            }
        }
}
 

 

      这里想说的就是其中体现到的逻辑,一定是在没有绑定之前处理的defaultLocalAddresses。几次synchronized保证的线程安全是很重要的,如果需要继承这些类的时候是记得利用这两处的synchronized
 和知道基础方法中有使用synchronized都是很重要的。

      bind是这个方法:public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException,需要提一下的就是最后这一句:

 

 

if (activate) {
      getListeners().fireServiceActivated();
}

     与前面的提到的Mina事件驱动机制联系起来了,最终调用的方法的注释为:Invoked when a new service is activated by an IoService.

 

(发现篇幅有点太长了,就分成两部分吧,最后一个抽象类和一个最终的实现类放在下一篇)

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    apache-mina源码

    通过阅读和分析`apache-mina-2.0.16`的源码,我们可以深入理解MINA的设计思想,学习如何构建高效的网络服务,并能根据自己的需求定制和扩展MINA的功能。对于想要从事网络编程或系统架构设计的开发者来说,研究MINA...

    MINA源码与例子

    通过阅读源码,你可以深入了解MINA的设计思想和实现细节,例如如何实现非阻塞I/O、过滤器链的工作原理、以及如何自定义协议编码解码器等。 学习MINA源码可以帮助你: 1. **理解网络编程**:MINA是基于Java NIO实现...

    Mina源码解析

    Mina的核心设计理念之一是过滤器链(Filter Chain),它借鉴了Servlet的过滤器模型。每个过滤器都可以在数据传输过程中进行拦截和处理,如数据编码、解码、安全检查等。过滤器之间通过`Filter.nextFilter`方法串联...

    MINA源码分析,内涵类的讲解

    MINA(Multipurpose Infrastructure for Network Applications)是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。MINA的目标是让开发者能够专注于应用层的逻辑,而...

    mina2.0源码svn地址

    Mina2.0作为Mina的重要版本之一,不仅继承了之前版本的优点,还在性能、易用性和稳定性方面进行了大量的改进。它支持多种协议(如TCP/IP、UDP等),并提供了丰富的功能模块,使得开发基于网络的应用程序变得更加简单...

    Mina2源码分析

    ### Mina2源码分析——核心模块解析 #### 概述 Mina2是一个高性能、可扩展的网络应用框架,支持多种传输协议如TCP、UDP等,并提供了丰富的API供开发者使用。本文旨在深入剖析Mina2的核心部分,帮助读者更好地理解和...

    mina 源码

    在分析"apache-mina-2.0.4"源码时,我们可以深入理解其设计理念、架构以及核心组件。 1. **MINA的设计理念** MINA的核心理念是异步非阻塞I/O,它利用Java NIO(New I/O)库来实现高效的网络通信。通过非阻塞I/O,...

    mina源码+例子mina-2.0.0-M6.zip

    2. **分析源码**:通过阅读MINA的源码,了解其内部处理流程,如读写事件的处理、过滤器链的工作方式等。 3. **编写简单的应用**:从例子入手,创建一个简单的MINA服务器和客户端,理解它们之间的通信机制。 4. **...

    MINA 2.0.9源码

    MINA 2.0.9是该框架的一个版本,其源码提供了深入理解MINA工作原理的机会,有助于开发者进行定制化开发或优化。 MINA的核心特性在于它的异步IO模型,它采用了NIO(Non-blocking I/O)技术,使得应用程序在处理大量...

    Mina 1.1.7 示例源码(apache.mina.example)

    这个压缩包"apache.mina.example"包含了一系列的示例项目,可以帮助开发者快速上手并深入理解Mina。 1. **Mina框架概述** Mina(Minimum Asynchronous Network)是一个基于Java的网络通信库,它提供了简单、高性能...

    Mina 框架源码解析-构建简单通信程序

    本篇文章将深入解析Mina框架的源码,通过构建一个简单的心跳通信程序,来理解其核心机制。 首先,我们来看一下心跳通信的基本概念。在分布式系统中,心跳机制是保持连接状态和检测网络故障的重要手段。心跳包是周期...

    Java mina2源码

    通过深入研究Mina2的源码,我们可以了解到如何优化网络通信性能,如何处理大规模并发连接,以及如何设计和实现自己的网络协议。源码分析还能帮助我们理解Java的多线程、并发控制和事件驱动编程等高级特性,提升我们...

    mina新手教程源码 mina+springboot+idea最简单的案例。

    mina新手案例,mina新手教程源码 mina+springboot最简单的案例。用的IDEA * mina服务端 * 1、添加@Controller注解和 @PostConstruct注解,代表启动springboot项目时也调用该类下的该方法, * 启动springboot项目...

    JAVA mina 框架源码

    深入研究Mina源码,我们可以学习到以下关键知识点: 1. **事件驱动模型**:Mina基于事件驱动模型,当网络事件发生时,如数据到达或连接建立,会触发相应的事件处理器,使得程序响应及时高效。 2. **异步编程**:...

    mina2 源码 mina

    对于有经验的开发者,深入源码可以提供对MINA内部机制的洞察,从而更好地优化和定制自己的网络服务。 总之,Apache MINA2是一个强大的网络应用框架,它的源码和实例是宝贵的教育资源,有助于提升Java网络编程能力,...

    mina框架源码及jar包

    在本压缩包"apache-mina-2.0.4"中,包含的是Mina框架的源码和jar包,这为开发者深入理解Mina的工作机制以及自定义功能提供了便利。 **Mina框架的核心概念:** 1. **I/O模型**:Mina基于NIO(Non-blocking I/O)...

    Mina 2.0快速入门与源码解析

    `IoAcceptor` 接口是 Mina 2.0 中的核心接口之一,它代表了一个网络服务端点。通过该接口可以设置各种处理器、配置参数以及绑定端口等操作。 **2.2 FilterChain** 在 Mina 2.0 中,FilterChain 是一种非常重要的...

    apache-mina-2.0.4.rar_apache mina_mina

    1. **Filter Chain**:Mina的核心设计模式之一是过滤器链。每个连接都有一系列过滤器,它们按照顺序处理入站和出站事件。过滤器可以实现特定功能,如数据编码解码、安全验证、性能监控等。 2. **Session**:Session...

    一个应用mina的实例(源码)

    Apache Mina是一个开源项目,它提供了一个高度可扩展和高性能的网络通信框架。这个实例是基于Mina框架构建的一个应用程序,名为"BTUSimulator",它可能是用来模拟某种网络通信或者服务的。让我们深入探讨一下Mina...

    apache下的mina框架的源码

    通过阅读源码,你可以了解MINA如何实现这些功能,并学习如何自定义过滤器以满足特定需求。例如,你可以看到如何创建一个新的过滤器来实现数据压缩,或者如何实现一个自定义的协议解码器。此外,MINA的异步IO机制使得...

Global site tag (gtag.js) - Google Analytics