Apache MINA 2 是一个开发高性能和高可伸缩性网络应用程序的网络应用框架。它提供了一个抽象的事件驱动的异步 API,可以使用 TCP/IP、UDP/IP、串口和虚拟机内部的管道等传输方式。

首先,mina server端acceptor启动方法:

1、NioSocketAcceptor.bind(InetSocketAddress)或者NioSocketAcceptor.bind(SocketAddress...)方法。

 

例如:

acceptor.bind(new InetSocketAddress(1234));  

mina底层的调用链:

NioSocketAcceptor.bind(InetSocketAddress)-->
AbstractIoAcceptor.bind(SocketAddress localAddress) -->
AbstractIoAcceptor.bind(Iterable<? extends SocketAddress> localAddresses)-->
AbstractPollingIoAcceptor.bindInternal(List<? extends SocketAddress> localAddresses)-->
AbstractPollingIoAcceptor.startupAcceptor()

1、创建线程Acceptor线程-->

2、AbstractIoService.executeWorker(Runnable worker)(提交线程到线程池)

acceptor线程启动run()

1、初始化acceptor端的Selector,即NioSocketAcceptor.Selector

2、NioSocketAcceptor.open(SocketAddress localAddress)

// Register the channel within the selector for ACCEPT event
channel.register(selector, SelectionKey.OP_ACCEPT);

acceptor工作流程:

一、IoService类图

如上图所示,IOService层根据不同的角色又分为IOAcceptor(服务端左半部分)和IOConnector (客户端右半部分),分别用于接受连接与请求连接操作。

二、服务端

2.1、IoAcceptor接口

IoAcceptor相当于是对ServerSocketChannel的封装,最重要的两个操作是绑定(解绑)与接受连接,IoAcceptor接口中有多个重载的bind()方法,当然呼应有多个重载的unbind()方法。

public interface IoAcceptor extends IoService {
void bind() throws IOException;
void bind(SocketAddress localAddress) throws IOException;
void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException;
void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException;
void unbind();
void unbind(SocketAddress localAddress);
void unbind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses);
void unbind(Iterable<? extends SocketAddress> localAddresses);
}

2.2、IoAcceptor的实现类AbstractIOAcceptor

  IoAcceptor其方法的实现在抽象类AbstractIOAcceptor的bind方法中,这个方法在做了参数检查等操作后,将真正的绑定操作交给abstract bindInternal()来完成。对于bindInternal有基于TCP/IP,UDP/IP,VMPipe三种实现类,分别对应

  • AbstractPollingIoAcceptor对应TCP/IP
  • AbstractPollingConnectionlessIoAcceptor对应UDP/IP
  • VmPipeAcceptor对应串口和虚拟机内部的管道

以TCP/IP为例来看绑定过程,见AbstractPollingIoAcceptor.java的源码:

protected final Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception {
// Create a bind request as a Future operation. When the selector
// have handled the registration, it will signal this future.
AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses); // adds the Registration request to the queue for the Workers
// to handle
registerQueue.add(request); // creates the Acceptor instance and has the local
// executor kick it off.
startupAcceptor(); // As we just started the acceptor, we have to unblock the select()
// in order to process the bind request we just have added to the
// registerQueue.
try {
lock.acquire(); // Wait a bit to give a chance to the Acceptor thread to do the select()
Thread.sleep(10);
wakeup();
} finally {
lock.release();
} // Now, we wait until this request is completed.
request.awaitUninterruptibly(); if (request.getException() != null) {
throw request.getException();
} // Update the local addresses.
// setLocalAddresses() shouldn't be called from the worker thread
// because of deadlock.
Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>(); for (H handle : boundHandles.values()) {
newLocalAddresses.add(localAddress(handle));
} return newLocalAddresses;

主要做了以下几件事情:
1、将绑定请求放入registerQueue中
2、启动Acceptor,从Acceptor类的run方法可以看到,这一步会阻塞在Acceptor选择器的选择操作中
3、调用wakeup让选择器返回
4、等待请求处理完成,这一步会阻塞在ready变量中,当ready变量为true时才会返回,当接受连接后ready会被设置为true.

现在重点看一下AbstractPollingIoAcceptor$Acceptor的run方法:

        public void run() {
assert (acceptorRef.get() == this); int nHandles = 0; // Release the lock
lock.release(); while (selectable) {
try {
// Detect if we have some keys ready to be processed
// The select() will be woke up if some new connection
// have occurred, or if the selector has been explicitly
// woke up
int selected = select(); // this actually sets the selector to OP_ACCEPT,
// and binds to the port on which this class will
// listen on
nHandles += registerHandles(); // Now, if the number of registred handles is 0, we can
// quit the loop: we don't have any socket listening
// for incoming connection.
if (nHandles == 0) {
acceptorRef.set(null); if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
assert (acceptorRef.get() != this);
break;
} if (!acceptorRef.compareAndSet(null, this)) {
assert (acceptorRef.get() != this);
break;
} assert (acceptorRef.get() == this);
} if (selected > 0) {
// We have some connection request, let's process
// them here.
processHandles(selectedHandles());
} // check to see if any cancellation request has been made.
nHandles -= unregisterHandles();
} catch (ClosedSelectorException cse) {
// If the selector has been closed, we can exit the loop
break;
} catch (Throwable e) {
ExceptionMonitor.getInstance().exceptionCaught(e); try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
ExceptionMonitor.getInstance().exceptionCaught(e1);
}
}
} // Cleanup all the processors, and shutdown the acceptor.
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();
}
}
}
}

(1)、selector被wakeup唤醒后,调用registerHandles方法从registerQueue中取出请求依次调用open方法

    private int registerHandles() {
for (;;) {
// The register queue contains the list of services to manage
// in this acceptor.
AcceptorOperationFuture future = registerQueue.poll(); if (future == null) {
return 0;
} // We create a temporary map to store the bound handles,
// as we may have to remove them all if there is an exception
// during the sockets opening.
Map<SocketAddress, H> newHandles = new ConcurrentHashMap<SocketAddress, H>();
List<SocketAddress> localAddresses = future.getLocalAddresses(); try {
// Process all the addresses
for (SocketAddress a : localAddresses) {
H handle = open(a);
newHandles.put(localAddress(handle), handle);
} // Everything went ok, we can now update the map storing
// all the bound sockets.
boundHandles.putAll(newHandles); // and notify.
future.setDone();
return newHandles.size();
} catch (Exception e) {
// We store the exception in the future
future.setException(e);
} finally {
// Roll back if failed to bind all addresses.
if (future.getException() != null) {
for (H handle : newHandles.values()) {
try {
close(handle);
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
} // TODO : add some comment : what is the wakeup() waking up ?
wakeup();
}
}
}
}

open方法完成了ServerSocket的绑定和注册(NioSocketAcceptor.open(SocketAddress localAddress)方法如下)

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

(2)、从(1)中可以知道selector上注册了ServerSocketChannel的OP_ACCEPT键,注册后nHandles==0,selected==0,进行下一次循环,同样是阻塞在select方法上
(3)、当连接到来时,select方法返回,selected>0,执行processHandles方法

        private void processHandles(Iterator<H> handles) throws Exception {
while (handles.hasNext()) {
H handle = handles.next();
handles.remove(); // Associates a new created connection to a processor,
// and get back a session
S session = accept(processor, handle); if (session == null) {
continue;
} initSession(session, null, null); // add the session to the SocketIoProcessor
session.getProcessor().add(session);
}
}

该方法在完成真正的接受连接操作后,创建session并扔到processor中,后续的工作交给processor来完成。每个session中其实有一个SocketChannel,这个socketChannel实际上是被注册到了processor的selector上。注册代码在NioProcessor类中可以找到

总结一下:Acceptor线程专门负责接受连接,在其上有一个selector,轮询是否有连接建立上来,当有连接建立上来,调用ServerSocketChannel.accept方法来接受连接,这个方法返回一个session对象,然后将这个session对象加入processor中,由processor遍历每个session来完成真正的IO操作。processor上也有一个selector与一个Processor线程,selector用于轮询session,Processor线程处理每个session的IO操作。

Mina2中IoService的更多相关文章

  1. mina2中IoHandler

    IoHandler 当我们通过IoSession执行相关操作的时候,如写数据,这些事件会触发Mina框架抽象的IoService实例,从而调用Mina框架底层的相关组件进行处理.这时,配置的IoHan ...

  2. mina2中的session

    简介 session类图 Mina每建立一个连接同时会创建一个session对象,用于保存这次读写需要用到的所有信息.从抽象类AbstractIoSession中可以看出session具有如下功能: ...

  3. mina2中的线程池

    一.Mina中的线程池模型 前面介绍了Mina总体的层次结构,那么在Mina里面是怎么使用Java NIO和进行线程调度的呢?这是提高IO处理性能的关键所在.Mina的线程调度原理主要如下图所示: A ...

  4. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  5. mina2 笔记

    http://www.iteye.com/topic/1112123 http://dongxuan.iteye.com/blog/901689 http://scholers.iteye.com/b ...

  6. mina socket底层主流程源码实现

    一,mina的架构 mina 架构可以大致分为三部分,ioService ,ioFilterChain , IoHandler   ioService:用于接受服务或者连接服务,例如socket 接收 ...

  7. MINA学习汇总

    MINA学习汇总 Apache Mina Server 是一个网络通信应用框架,用于开发高性能和高可用性的网络应用程序.它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(然,也可以提供JAVA ...

  8. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  9. mina2

      远程通信 Mina2 学习笔记 作者:李少华 邮箱:xiaosanshaoli@126.com QQ:305409913 2010-12-23   初稿 引言... 1 一.       Mina ...

随机推荐

  1. windows10 搜索桌面搜索功能失效的解决

    windows桌面的搜索框用起来很方便,很多时候直接把不常用的程序的快捷方式删掉,直接从搜索框搜索就可以,但是这两天突然不能用了,今天晚上找了一下原因,终于弄好了. 参考知乎上面的陈滔滔的方法: ht ...

  2. SpringMVC实现AJax以及RestFull风格

    RestFull风格就是url路径中不能出现?不能带参数,如https://www.baidu.com/user/item/1234这个格式,也叫url资源定位 1.需要在web.xml中开启put, ...

  3. HBase-存储-KeyValue格式

    HBase-存储-KeyValue格式 本质上,HFile中的每个KeyValue都是一个低级的字节数组,它允许零复制访问数据. KeyValue格式如下 该结构以两个分别表示键长度(Key Leng ...

  4. 域名解析中TTL是什么意思

    在做域名解析的时候都会看到一个叫“TTL”的值,一般都有一个默认的值,不过不同注册商默认的值也会不一样,常见的是3600和7200这两个值. 另外ping的时候也可以看到“TTL=XXX”的字样,(如 ...

  5. mssql 函数STUFF 的用法

    DECLARE @limitDay INT;SET @limitDay = 92;IF DATEDIFF(DAY, '2017-12-13 00:00:00', '2017-12-13 18:00:0 ...

  6. ubuntu上安装nodejs

    目录: 1. nodejs的下载 2. 解压和安装 3. 安装过程中出现过的问题 4. 总结 1. nodejs的下载 我刚开始没有linux系统,于是安装了nodejs的windows版本进行学习. ...

  7. linux下图形字符的命令

    banner sudo apt-get install sysvbanner banner song 若更改字体可以使用banner song printerbanner -w 50 A toilet ...

  8. mac中的echo颜色输出

    mac: echo "\033[1;36mSister Lin Fall from the Sky\033[0m" ubuntu: echo -e "\e[1;36mSi ...

  9. application.yml配置log日志

    #日志文件的配置logging: pattern: console: "%d - %msg%n" file: /var/log/sell.log 注解@Slf4j

  10. js字符串和数组操作,容易混淆的方法总结(slice、substring、substr、splice)

    平时工作中,很少静下心来总结基础知识,总觉得自己会用了,有点飘了,直到碰壁之后才懂得基础知识的重要性.大牛告诉我,一次写对,是不是可以不用F12去调试了?是不是省了时间?简直是面红耳赤,无地自容.在这 ...