MINA源码读

MINA框架第一接口是org.apache.mina.core.service.IoService, 接收和发送两端的接口IoAcceptor, IoConnector都继承于它,它提供的方法主要有:


TransportMetadata getTransportMetadata();
底层传输的元数据,如网络服务提供者(NIO/APR/RXTX)等

void addListener(IoServiceListener listener);
void removeListener(IoServiceListener listener);
增加或删除相关联的监听事件

IoHandler getHandler();
void setHandler(IoHandler handler);
设置相关联的处理器

void setFilterChainBuilder(IoFilterChainBuilder builder);
IoFilterChainBuilder getFilterChainBuilder();
设置一个filter链,这个链是此service中所有IoSession相关联的IoFilterChain
等等.

 

IoAcceptor主要接收来自客户端的请求并处理,其核心方法就是
void bind(SocketAddress localAddress);
void unbind(SocketAddress localAddress);

 

IoConnector主要是连接客户端,反馈相应的信息,其核心方法是
ConnectFuture connect(SocketAddress remoteAddress);

 

抽象类AbstractIoService部分实现IoService,而抽象类AbstractIoAcceptor,AbstractIoConnector则都继承AbstractIoService,且分别实现对应的接口IoAcceptor, IoConnector.

 

IoAcceptor的实例是在MINA启动时产生的,只有一个,而其要处理的session则会借助IoProcessor实现多线程,其实现类SimpleIoProcessorPool在IoAcceptor初始化时new出来,形成个processor池,它在accept每绑定一个socketAddress时会在processor池中拉一个出来处理session(process的具体实现在AbstractPollingIoProcessor里,一个process对应多个session,每个session对应每次的请求,而每个process有一个Selector,通过其来实现多路复用).

 

下面是从MINA源码中取得的一个测试:

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.OrderedThreadPoolExecutor;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * TODO : Add documentation
 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
 *
 */
public class MinaRegressionTest extends IoHandlerAdapter {
  //private static final Logger logger = LoggerFactory.getLogger(MinaRegressionTest.class);

  public static final int MSG_SIZE = 5000;
  public static final int MSG_COUNT = 10;
  private static final int PORT = 23234;
  private static final int BUFFER_SIZE = 8192;
  private static final int TIMEOUT = 10000;

  public static final String OPEN = "open";

  public SocketAcceptor acceptor;
  public SocketConnector connector;

  private final Object LOCK = new Object();

  private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {
    public Thread newThread(final Runnable r) {
      return new Thread(null, r, "MinaThread", 64 * 1024);
    }
  };

  private OrderedThreadPoolExecutor executor;

  public static AtomicInteger sent = new AtomicInteger(0);


  public MinaRegressionTest() throws IOException {
    executor = new OrderedThreadPoolExecutor(
      0,
      1000,
      60,
      TimeUnit.SECONDS,
      THREAD_FACTORY);

    acceptor = new NioSocketAcceptor(Runtime.getRuntime().availableProcessors() + 1);
    acceptor.setReuseAddress( true );
    acceptor.getSessionConfig().setReceiveBufferSize(BUFFER_SIZE);

    acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(executor));
    acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyProtocolCodecFactory()));

    connector = new NioSocketConnector(Runtime.getRuntime().availableProcessors() + 1);

    connector.setConnectTimeoutMillis(TIMEOUT);
    connector.getSessionConfig().setSendBufferSize(BUFFER_SIZE);
    connector.getSessionConfig().setReuseAddress( true );
  }

  public void connect() throws Exception {
    final InetSocketAddress socketAddress = new InetSocketAddress("0.0.0.0", PORT);

    acceptor.setHandler(new MyIoHandler(LOCK));

    acceptor.bind(socketAddress);
    connector.setHandler(this);

    final IoFutureListener<ConnectFuture> listener = new IoFutureListener<ConnectFuture>() {
      public void operationComplete(ConnectFuture future) {
        try {System.out.println( "Write message to session " + future.getSession().getId() );
          final IoSession s = future.getSession();
          IoBuffer wb = IoBuffer.allocate(MSG_SIZE);
          wb.put(new byte[MSG_SIZE]);
          wb.flip();
          s.write(wb);
        } catch (Exception e) {
          //System.out.println("Can't send message: {}", e.getMessage());
        }
      }
    };

    for (int i = 0; i < MSG_COUNT; i++) {
      ConnectFuture future = connector.connect(socketAddress);
      future.addListener(listener);
    }

    synchronized (LOCK) {
      LOCK.wait(50000);
    }

    connector.dispose();
    acceptor.unbind();
    acceptor.dispose();
    executor.shutdownNow();

    System.out.println("Received: " + MyIoHandler.received.intValue());
    System.out.println("Sent: " + sent.intValue());
    System.out.println("FINISH");
  }

  @Override
  public void exceptionCaught(IoSession session, Throwable cause) {
    if (!(cause instanceof IOException)) {
      //logger.error("Exception: ", cause);
    } else {
      System.out.println("I/O error: " + cause.getMessage());
    }
    session.close(true);
  }

  @Override
  public void messageSent(IoSession session, Object message) throws Exception {
    sent.incrementAndGet();
  }

  public static void main(String[] args) throws Exception {
    System.out.println("START");
    new MinaRegressionTest().connect();
  }
}

 

其中MinaRegressionTest构造函数其实就是MINA启动的代码,其初始化个NioSocketAcceptor,而方法connect()则是客户端要写的代码,设置handler,bind socket,最后在unbind().
具体来说,在NioSocketAcceptor初始化时会初始化一下信息:SimpleIoProcessorPool(初始化时会生成Runtime.getRuntime().availableProcessors() + 1大小的池),Executor线程池,IoServiceListener,IoFilterChain等.
而在执行bind()时,其最终是调用AbstractPollingIoAcceptor子类Acceptor的processHandles()方法来连接process和session,handle,进而处理,这里的实现就是基于Java NIO实现的Reactor模式的一个简单实现.

 

另看到其他一篇不错的关于MINA源码的文章(http://chillwarmoon.iteye.com ):


一  读源码时,首先要弄清楚代码所涉及到的重要模型及其之间的关系,从整体架构方面理解其组成。

 

1.对于IOService的实现NioSocketConnector和NioSocketAcceptor来说,都持有selector的引用,本身都有一个固定的线程池executor,用来执行Connector或者Acceptor任务。

2.除此之外,IoService还有对Processor池的引用,该池中的元素processor的数量是机器的cpu核数+1。

3.processor池中默认元素为NioProcessor对象,每个元素都有一个selector,而且有一个executor线程池,用来执行Processor任务。

 

二  分析框架的初始化方式,注意不要陷入技术细节,仍然是整体把握。

 

4.IoService初始化时,需要通过具体的实现提供的transportMetaData,来判断其中规定的session配置类(例如:DefaultSocketSessionConfig)是否来自接口SessionConfig。

5.IoService初始化时,建立与监听器容器IoServiceListenerSupport的双向关联,注册匿名内部实现serviceActivationListener到监听器容器。

PS:Mina框架比起Tomcat源码来说简单很多,Tomcat启动逻辑就花了很长时间来读。

 

三  要充分利用eclipse中提供给我们查找源码的方法,例如Ctrl+T,Ctrl+H,Ctrl+.,Alt+Shift+o等等。

 

四  框架初始化后,根据框架的使用方式,来逐步分析代码。

 

6.关键是对Session的理解:
持有对IoService,Processor池,SocketChannel,SessionConfig和IoService.IoHandler的引用。
在初始化session时,将config配置到了SocketChannel中。
深入理解:
在每次connect后,都会建立与之对应的session(session与channel是1:1关系),而且每个session都加入了processor中,但是都是持有的对一个IoHandler的引用。

将session加入到processor池:
该processor元素的选取是依次在池中选择的。然后将该processor元素加入到了session的attribute中。

也就是说,不同的session可能加入到池中的一个processor元素,一个processor元素有一个selector和一个Processor线程,该线程可以对processor元素对应的这些session进行处理。

7.Processor线程:
(1)如果有newSession,则将channel注册事件SelectionKey.OP_READ到该processor元素的selector
(2)将getFilterChainBuilder中的链节点加入到该session的链中
(3)fire相应的SessionCreate和SessionOpen的chain节点
(4)如果SelectionKey.OP_READ准备好,则read数据,如果session对应的发送队列有数据,则write数据

如果建立的session都放在了一个processor元素里,则handler只能够被一个线程访问,否则handler可能会被多个线程执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值