Mina源码分析——IoService

这两天看了下Mina和Netty的源码,想比较来说Mina的结构简洁易懂。Mina和Netty出自同一人,而Netty是作者对Mina的重构版,改进了很多。如果学习网络编程的话,个人建议从Mina开始,学完Mina后再看Netty,学习曲线会变得很平滑;同时还能深刻理解到作者改进点。
   先看下Mina的结构图,如果之前做过java的web开发,熟悉servlet规范,看到这个结构图,会觉得非常亲切。这个结构描述了基本的网络开发结构。

  • IoService  服务端和客户端I/O 操作的抽象,服务端为IoAcceptor,客户端为IoConnector
  • IoSession  封装了服务端与客服端连接的会话信息
  • IoFilterChain IoFilter处理链
  • IoFilter   对服务端和客户端交互的数据做处理
  • IoHandler  业务处理
好了,说了那么多还是先分析代码吧。Mina的客户端和服务端开发会略有不同,因为java网络编程的本身就是如此。我们先从分析服务器端开发入手。这里引用了Mina example中的Timer server,它的逻辑非常简单,就是接收到客户端的请求后,返回服务器的当前时间。
TimerServerHandler.java
public class TimeServerHandler extends IoHandlerAdapter {

  @Override
  public void messageReceived(IoSession session, Object message) throws Exception {
    String msg = (String) message;
    if ("quit".equals(msg.trim())) {
      System.out.println("client " + session.getRemoteAddress() + " quited!");
      session.close(false);
      return;
    }

    Date date = new Date();
    session.write(date.toString());
    System.out.println("message written...");
  }

}
开启服务器
private static final int SERVER_PORT = 8888;

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

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

  acceptor.setHandler(new TimeServerHandler());

  acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
  acceptor.getSessionConfig().setReadBufferSize(2048);

  acceptor.bind(new InetSocketAddress(SERVER_PORT));
}
1、先创建一个IoAcceptor实例,这里创建的是一个基于tcp的Java NIO版的IoAccptor
2、往FilterChain中追加了 LoggingFilter 和TextLineCodecFactory,LoggingFilter 可以对客户端的请求和服务器端的响应日志记录。TextLineCodecFactory 是一个协议编码解码器的工厂,就是将字节流与java中的String之间的相互转换。后面会详细介绍。
3、添加一个TimerServerHandler实例。TimeServerHandler 处理的逻辑就是在接收到客户端发送过来的字符串信息后,判断如果是quit,关闭与客户端的链接。不是的就返回服务器的当前时间的字符串。
4、设置一些IoSession的可配属性
5、绑定到一个端口上,开始监听客户端的请求。
上面我们只写了一个TimerServerHandler,并在messageReceived 方法中定义了自己的业务处理就轻松的完成了一个服务器端的开发,而不用去关心底层的链接和I/O处理,这就是mina的魅力所在,让开发人员从处理容易出错的I/O操作中解放出来。
现在我们逐个分析代码中出现的一些类和方法。先看一个IoService的继承体系。Mina提供了丰富的实现,支持很多协议,IoService的继承体系没有下面简单。简单起见,上面的图中值画出了Server端的结构,分析IoServeric先从IoAcceptor入手,而且就分析我们熟悉的java nio相关的类。

IoAcceptor是IoService在服务器端的一个抽象。先从接口的功能开始分析:
public interface IoService {

  TransportMetadata getTransportMetadata();

  void addListener(IoServiceListener listener);

  void removeListener(IoServiceListener listener);

  boolean isDisposing();

  boolean isDisposed();

  void dispose();

  void dispose(boolean awaitTermination);

  IoHandler getHandler();

  void setHandler(IoHandler handler);

  Map<Long, IoSession> getManagedSessions();

  int getManagedSessionCount();

  IoSessionConfig getSessionConfig();

  IoFilterChainBuilder getFilterChainBuilder();

  void setFilterChainBuilder(IoFilterChainBuilder builder);

  DefaultIoFilterChainBuilder getFilterChain();

  boolean isActive();

  long getActivationTime();

  Set<WriteFuture> broadcast(Object message);

  IoSessionDataStructureFactory getSessionDataStructureFactory();

  void setSessionDataStructureFactory(IoSessionDataStructureFactory sessionDataStructureFactory);

  int getScheduledWriteBytes();

  int getScheduledWriteMessages();

  IoServiceStatistics getStatistics();
}
从接口的方法上分析可以了解到IoService的主要功能:
1、获取链接通信的元数据
2、IoService维护一个IoServiceListener的列表,IoServiceListener顾名思义,就是对IoService相关的事件进行监听。
3、关闭链接
4、一个IoService对应有一个IoHandler
5、IoService维护这一个IoSession的map
6、一个IoService对应一个FilterChain
7、支持广播功能
8、管理IoSession的中的数据结构
9、统计功能
IoAcceptor     在IoService基础上扩展了绑定和解绑SocketAddress的功能。
SocketAcceptor     在IoAcceptor的基础上将SocketAddress 具体化到InetSocketAddress,同时将IoSessionConfig具体化到SocketSessionConfig。提供了reuseaddress 和backlog的设置。关于backlog在SocketServer中的文档描述是
引用


backlog requested maximum length of the queue of incoming connections.

抽象类分析:
AbstractIoService 提供了IoService中的一些默认实现。
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() + ")");
  }

  listeners = new IoServiceListenerSupport(this);
  listeners.add(serviceActivationListener);

  this.sessionConfig = sessionConfig;

  ExceptionMonitor.getInstance();

  if (executor == null) {
    this.executor = Executors.newCachedThreadPool();
    createdExecutor = true;
  } else {
    this.executor = executor;
    createdExecutor = false;
  }

  threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
}
从AbstractIoService 构造函数来分析可以得知
1、AbstractIoService中只定义了上面的构成函数,没有显式定义无参构造函数,所以在子类的初始化在肯定会调用super(IoSessionConifg,Executor),再从上面构造函数前面的判断来看,IoSessionConfig,TransportMetadata都是由子类构造函数传入。Executor 参数子类可传可不传,不传默认Executors.newCachedThreadPool();创建。
2、IoServiceListener 列表的管理交给了IoServiceListenerSupport去处理。并添加了一个IoService激活事件的监听器。
3、创建了一个ExceptionMonitor实例
4、定义了构造Acceptor处理线程名称的逻辑
构造函数之外,AbstractIoService 也定义了一些默认实现
1、IoFilterChain 默认交给DefaultIoFilterChainBuilder 创建
2、IoSessionDataStructureFactory 默认实现为 DefaultIoSessionDataStructureFactory
3、实现了dispose的基本逻辑,为什么说是基本逻辑呢?因为dispose调用的 dispose0方法是交由子类去实现的
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;
}
4、实现了broadcast
public final Set<WriteFuture> broadcast(Object message) {
  // Convert to Set.  We do not return a List here because only the
  // direct caller of MessageBroadcaster knows the order of write
  // operations.
  final List<WriteFuture> futures = IoUtil.broadcast(message,
      getManagedSessions().values());
  return new AbstractSet<WriteFuture>() {
    @Override
    public Iterator<WriteFuture> iterator() {
      return futures.iterator();
    }

    @Override
    public int size() {
      return futures.size();
    }
  };
}
AbstractIoAcceptor   继承自AbstractIoService并实现了IoAcceptor接口,主要的实现有:
1、增加一个可配置的SocketAddress 列表 defaultLocalAddresses ,以及这个列表的只读版本
2、增加一个已绑定的SocketAddress 列表
3、增加一个disconnectOnUnbind 配置,指定在Unbind时是否断掉与客户端的链接
4、实现了bind的基本逻辑,更具体的逻辑在bindInternal中交由子类去实现
public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
  if (isDisposing()) {
    throw new IllegalStateException("Already disposed.");
  }
  
  if (localAddresses == null) {
    throw new IllegalArgumentException("localAddresses");
  }
  
  List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
  
  for (SocketAddress a: localAddresses) {
    checkAddressType(a);
    localAddressesCopy.add(a);
  }
  
  if (localAddressesCopy.isEmpty()) {
    throw new IllegalArgumentException("localAddresses is empty.");
  }
  
  boolean activate = false;
  synchronized (bindLock) {
    synchronized (boundAddresses) {
      if (boundAddresses.isEmpty()) {
        activate = true;
      }
    }

    if (getHandler() == null) {
      throw new IllegalStateException("handler is not set.");
    }
    
    try {
      Set<SocketAddress> addresses = bindInternal( localAddressesCopy );
      
      synchronized (boundAddresses) {
        boundAddresses.addAll(addresses);
      }
    } catch (IOException e) {
      throw e;
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable e) {
      throw new RuntimeIoException(
          "Failed to bind to: " + getLocalAddresses(), e);
    }
  }
  
  if (activate) {
    getListeners().fireServiceActivated();
  }
}
5、实现了unbind的基本逻辑,更具体的逻辑在unbind0中交由子类实现
public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
  if (localAddresses == null) {
    throw new IllegalArgumentException("localAddresses");
  }
  
  boolean deactivate = false;
  synchronized (bindLock) {
    synchronized (boundAddresses) {
      if (boundAddresses.isEmpty()) {
        return;
      }

      List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
      int specifiedAddressCount = 0;
      
      for (SocketAddress a: localAddresses ) {
        specifiedAddressCount++;

        if ((a != null) && boundAddresses.contains(a) ) {
          localAddressesCopy.add(a);
        }
      }
      
      if (specifiedAddressCount == 0) {
        throw new IllegalArgumentException( "localAddresses is empty." );
      }
      
      if (!localAddressesCopy.isEmpty()) {
        try {
          unbind0(localAddressesCopy);
        } catch (RuntimeException e) {
          throw e;
        } catch (Throwable e) {
          throw new RuntimeIoException(
              "Failed to unbind from: " + getLocalAddresses(), e );
        }

        boundAddresses.removeAll(localAddressesCopy);
        
        if (boundAddresses.isEmpty()) {
          deactivate = true;
        }
      }
    }
  }

  if (deactivate) {
    getListeners().fireServiceDeactivated();
  }
}
AbstractPollingIoAcceptor<T extends AbstractIoSession, H>
泛参H在子类NioSocketAcceptor中替换为ServerSocketChannel,泛参T替换为NioSession。这样便于分析后面的代码。
主要实现了bind,accept,dispose ServerSocket的基本逻辑。父类AbstractIoService中的Executor主要用于执行ServerSocket的accept逻辑。一旦与客户端建立连接后,之后的I/O操作将交由IoProcessor去处理。关于ServerSocketChannel 的 select,open,close,accept都交由子类实现
私有构造函数定义了IoProcessor实例由外部注入,并初始化seelectable标记为true。具体的init()逻辑由子类去实现。
private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
    Executor executor, IoProcessor<NioSession> processor,
    boolean createdProcessor) {
  super(sessionConfig, executor);

  if (processor == null) {
    throw new IllegalArgumentException("processor");
  }
  //注入IoProcessor对象
  this.processor = processor;
  this.createdProcessor = createdProcessor;

  try {
    //初始化设置交给子类实现
    init();
    // 构造函数中设置标记为true,后面便可以s
    selectable = true;
  } catch (RuntimeException e) {
    throw e;
  } catch (Exception e) {
    throw new RuntimeIoException("Failed to initialize.", e);
  } finally {
    if (!selectable) {
      try {
        destroy();
      } catch (Exception e) {
        ExceptionMonitor.getInstance().exceptionCaught(e);
      }
    }
  }
}
其他几个重载的构造函数注入默认IoProcessor为SimpleIoProcessorPool实例
protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
    Class<? extends IoProcessor<T>> processorClass, int processorCount) {
  this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass,
      processorCount), true);
}
提供了一个轮询策略的Acceptor的实现
private class Acceptor implements Runnable {
  public void run() {
    // nHandles 表示已经open的ServerSocketChannel数量
    int nHandles = 0;
    //无限循环,接收客户端的链接请求,并处理,直到所有opened ServerSocketChannel都被close
    while (selectable) {
      try {
        //轮询获得
        int selected = select();
        //open ServerSocketChannel并增加nHandles
        nHandles += registerHandles();

        if (selected > 0) {
          // We have some connection request, let's process 
          // them here. 
          processHandles(selectedHandles());
        }

        //close ServerSocketChannel并减少nHandles
        nHandles -= unregisterHandles();
        //没有ServerSocketChannel在监听客户端的请求,跳出循环
        if (nHandles == 0) {
          synchronized (lock) {
            if (registerQueue.isEmpty()
                && cancelQueue.isEmpty()) {
              acceptor = null;
              break;
            }
          }
        }
      } catch (ClosedSelectorException cse) {
        break;
      } catch (Throwable e) {
        ExceptionMonitor.getInstance().exceptionCaught(e);

        try {
          Thread.sleep(1000);
        } catch (InterruptedException e1) {
          ExceptionMonitor.getInstance().exceptionCaught(e1);
        }
      }
    }

    if (selectable && isDisposing()) {
      selectable = false;
      try {
        if (createdProcessor) {
          processor.dispose();
        }
      } finally {
        try {
          synchronized (disposalLock) {
            if (isDisposing()) {
              destroy();
            }
          }
        } catch (Exception e) {
          ExceptionMonitor.getInstance().exceptionCaught(e);
        } finally {
          disposalFuture.setDone();
        }
      }
    }
  }

  private void processHandles(Iterator<ServerSocketChannel> handles) throws Exception {
    while (handles.hasNext()) {
      ServerSocketChannel handle = handles.next();
      handles.remove();
      // 接收客户端的请求,建立链接,返回对链接信息封装后的IoSession
      NioSession session = accept(processor, handle);
      
      if (session == null) {
        break;
      }
      // 初始化IoSession信息
      initSession(session, null, null);
      // 将连接的I/O(read,write,close etc)交给Processor线程处理
      session.getProcessor().add(session);
    }
  }
}

private int registerHandles() {
  //开启一个无限循环,不断从registerQueue队列中获取AcceptorOperationFuture,直到registerQueue为空
  for (;;) {
    AcceptorOperationFuture future = registerQueue.poll();
    
    if (future == null) {
      return 0;
    }

    // 创建一个临时的map以便在打开socket的时候出现异常及时释放资源
    Map<SocketAddress, ServerSocketChannel> newHandles = new ConcurrentHashMap<SocketAddress, ServerSocketChannel>();
    List<SocketAddress> localAddresses = future.getLocalAddresses();

    try {
      for (SocketAddress a : localAddresses) {
        //遍历所有的SocketAddress,open ServerSocketChannel
        ServerSocketChannel handle = open(a);
        newHandles.put(localAddress(handle), handle);
      }
      // 未出现异常,将所有open成功的ServerSocketChannel放到boundHandles
      boundHandles.putAll(newHandles);
      // 设置异步处理完成
      future.setDone();
      // 返回open成功的ServerSocketChannel的数量
      return newHandles.size();
    } catch (Exception e) {
      future.setException(e);
    } finally {
      //在open时出现了异常,释放相应的 资源
      if (future.getException() != null) {
        for (ServerSocketChannel handle : newHandles.values()) {
          try {
            close(handle);
          } catch (Exception e) {
            ExceptionMonitor.getInstance().exceptionCaught(e);
          }
        }
        
        wakeup();
      }
    }
  }
}

// 关闭ServerSocketChannel
private int unregisterHandles() {
  int cancelledHandles = 0;
  // 循环从cancelQueue队列中获取待关闭的ServerSocketChannel,直到cancelQueue清空
  for (;;) {
    AcceptorOperationFuture future = cancelQueue.poll();
    if (future == null) {
      break;
    }

    for (SocketAddress a : future.getLocalAddresses()) {
      // 先从已绑定的ServerSocketChannel列表中移除
      ServerSocketChannel handle = boundHandles.remove(a);
      
      if (handle == null) {
        continue;
      }

      try {
        //关闭ServerSocketChannel,真正的实现交给子类
        close(handle);
        wakeup();
      } catch (Throwable e) {
        ExceptionMonitor.getInstance().exceptionCaught(e);
      } finally {
        cancelledHandles++;
      }
    }
    //
    future.setDone();
  }
  //返回已关闭的ServerSocketChannel的数量 
  return cancelledHandles;
}
NioSocketAcceptor
基于tcp协议的java nio版IoAcceptor实现,到这里已经实现所有的网络I/O操作。
//默认的ServerSocket的backlog属性为50
private int backlog = 50;
// 默认reuseAddress为false
private boolean reuseAddress = false;
// java nio 中的selector,状态改变多线程可见
private volatile Selector selector;

//构造函数出入的默认IoSessionConfig实现为DefaultSocketSessionConfig
public NioSocketAcceptor() {
  super(new DefaultSocketSessionConfig(), NioProcessor.class);
  ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
}
// 如果你熟悉java nio,看到这些代码是否有种豁然开朗的感觉呢?原来是这样的啊!
@Override
protected void init() throws Exception {
  selector = Selector.open();
}

@Override
protected void destroy() throws Exception {
  if (selector != null) {
    selector.close();
  }
}

public TransportMetadata getTransportMetadata() {
  return NioSocketSession.METADATA;
}

@Override
protected NioSession accept(IoProcessor<NioSession> processor,
    ServerSocketChannel handle) throws Exception {

  SelectionKey key = handle.keyFor(selector);
  
  if ((key == null) || (!key.isValid()) || (!key.isAcceptable()) ) {
    return null;
  }

  SocketChannel ch = handle.accept();
  
  if (ch == null) {
    return null;
  }

  return new NioSocketSession(this, processor, ch);
}

@Override
protected ServerSocketChannel open(SocketAddress localAddress)
    throws Exception {
  // Creates the listening ServerSocket
  ServerSocketChannel channel = ServerSocketChannel.open();
  
  boolean success = false;
  
  try {
    // This is a non blocking socket channel
    channel.configureBlocking(false);
  
    // Configure the server socket,
    ServerSocket socket = channel.socket();
    
    // Set the reuseAddress flag accordingly with the setting
    socket.setReuseAddress(isReuseAddress());
    
    // and bind.
    socket.bind(localAddress, getBacklog());
    
    // Register the channel within the selector for ACCEPT event
    channel.register(selector, SelectionKey.OP_ACCEPT);
    success = true;
  } finally {
    if (!success) {
      close(channel);
    }
  }
  return channel;
}

@Override
protected void close(ServerSocketChannel handle) throws Exception {
  SelectionKey key = handle.keyFor(selector);
  
  if (key != null) {
    key.cancel();
  }
  
  handle.close();
}

@Override
protected void wakeup() {
  selector.wakeup();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值