Mina框架IoHandler与IoProcessor详解

我们已经知道,IoHandler是开发网络应用程序的时候,与实际业务逻辑相关的组件,即属于Mina核心框架之外的应用层组件。从Mina 官方文档上,我们几乎没有看到对IoProcessor的说明,实际上IoProcessor对实际使用Mina框架的开发人员透明,无需你去了解它的实现逻辑,它在Mina中用来处理实际的I/O操作。
我们分析的思路是,先分别对IoHandler与IoProcessor进行单独分析,然后再阐述它们之间的不同以及联系。

  • IoHandler

当我们通过IoSession执行相关操作的时候,如写数据,这些事件会触发Mina框架抽象的IoService实例,从而调用Mina框架底层的相关组件进行处理。这时,配置的IoHandler就被用来处理Mina所触发的相关事件,处理这些事件的操作被抽象出来。
实际上,IoHandler的继承层次非常简单,也说明了基于Mina框架开发实际网络应用程序,对业务逻辑的处理也还是相对比较容易的。看一下 IoHandler的继承层次,如图所示:
Mina_IoHandler
IoHandler接口所定义的操作,一共定义了7个处理事件的操作,如下所示:

public interface IoHandler {
	void sessionCreated(IoSession session) throws Exception;
	void sessionClosed(IoSession session) throws Exception;
	void sessionIdle(IoSession session, IdleStatus status) throws Exception;
	void exceptionCaught(IoSession session, Throwable cause) throws Exception;
	void messageReceived(IoSession session, Object message) throws Exception;
	void messageSent(IoSession session, Object message) throws Exception;
}

因为IoHandler是一个接口,所以如果使用该接口我们就必须实现所有的方法,MIna通过使用IoHandlerAdapter来默认实现 IoHandler接口,并在IoHandlerAdapter中全部给出空实现,如果我们要开发自己的IoHandler,可以继承自IoHandlerAdapter,根据需要选择重写指定的处理Mina事件的方法,而对于你不感兴趣的方法就默认不给予实现(默认使用 IoHandlerAdapter的空实现)。
那么,Mina调用IoHandler的时机是什么呢?又是如何调用的呢?
其实,根据Mina的架构,我们知道,在客户端主动发起I/O操作请求以后,会等待Mina触发相应的事件,在经过一组IoFilter之后,在 IoFilter链的最后一个IoFilter被调用将要结束的时候,会调用我们注册的IoHandler实现,经过处理来满足实际业务逻辑需要。我们可以在DefaultIoFilterChain中看到一个内部IoFilter实现类TailFilter,在该类里调用了 IoHandler封装的逻辑,代码如下所示:

private static class TailFilter extends IoFilterAdapter {
	@Override
	public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
		try {
			session.getHandler().sessionCreated(session);
		} finally {
			// Notify the related future.
			ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
			if (future != null) {
				future.setSession(session);
			}
		}
	}

	@Override
	public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		session.getHandler().sessionOpened(session);
	}

	@Override
	public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
		AbstractIoSession s = (AbstractIoSession) session;
		try {
			s.getHandler().sessionClosed(session);
		} finally {
			try {
				s.getWriteRequestQueue().dispose(session);
			} finally {
				try {
					s.getAttributeMap().dispose(session);
				} finally {
					try {
						// Remove all filters.
						session.getFilterChain().clear();
					} finally {
						if (s.getConfig().isUseReadOperation()) {
							s.offerClosedReadFuture();
						}
					}
				}
			}
		}
	}

	@Override
	public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
		session.getHandler().sessionIdle(session, status);
	}

	@Override
	public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
		AbstractIoSession s = (AbstractIoSession) session;
		try {
			s.getHandler().exceptionCaught(s, cause);
		} finally {
			if (s.getConfig().isUseReadOperation()) {
				s.offerFailedReadFuture(cause);
			}
		}
	}

	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		AbstractIoSession s = (AbstractIoSession) session;
		if (!(message instanceof IoBuffer)) {
			s.increaseReadMessages(System.currentTimeMillis());
		} else if (!((IoBuffer) message).hasRemaining()) {
			s.increaseReadMessages(System.currentTimeMillis());
		}

		try {
			session.getHandler().messageReceived(s, message);
		} finally {
			if (s.getConfig().isUseReadOperation()) {
				s.offerReadFuture(message);
			}
		}
	}

	@Override
	public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
		session.getHandler().messageSent(session, writeRequest.getMessage());
	}

	@Override
	public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
		nextFilter.filterWrite(session, writeRequest);
	}

	@Override
	public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
		nextFilter.filterClose(session);
	}
}

在上面的代码中,正好调用了IoHandler接口定义的7个处理事件的方法。如果你还想知道IoFilterChain实例是在何时被调用的, 可以跟踪Mina的源码。
* IoProcessor

基于网络的端到端的通信,Mina通过一个IoSession对象(任何获取到一个IoSession实例的持有者都可以)来间接执行 I/O操作,如发送数据、读取数据等。在Mina内部,当一个IoSession调用对应的方法,则直接触发IoProcessor来对指定的事件进行处理,它基于Ractor模式来简化网络传输的实现(事实上,Java NIO就是基于Reactor模式实现,属于同步非阻塞IO模式)。
我们看一下IoProcessor相关类的继承关系,如图所示:
Diagram_Mina_IoProcessor
看到上面AbstractPollingConnectionlessIoAcceptor,我们知道,它同时也是IoService的实现,用于网络通信中的服务端,处理接收请求。可见,对于基于UDP/IP的传输,IoAcceptor和IoProcessor的处理是实现在一起的,可能实际处理的逻辑本身比较简单,放到一起能够更好地表达它们之间的紧密联系。
下面我们看一下IoProcessor接口的定义,如下所示:

public interface IoProcessor<S extends IoSession> {
	boolean isDisposing();
	boolean isDisposed();
	void dispose();
	void add(S session);
	void flush(S session);
	void write(S session, WriteRequest writeRequest);
	void updateTrafficControl(S session);
	void remove(S session);
}

我们根据上面接口总结一下,一个IoProcessor实际处理了如下内容:

  1. 添加IoSession实例,主要是使用IoProcessor内部的一个IoSession队列newSessions来存放。
  2. Flush指定IoSession实例到IoProcessor内部的flushingSessions队列。
  3. Write一个IoSession实例对应的WriteRequest,主要是将一个WriteRequest加入到 IoSession实例所持有的WriteRequestQueue writeRequestQueue队列。至于加入到队列的请求何时处理,其实我们可以参考IoProcessor的实现AbstractPollingIoProcessor 类,其内部有一个org.apache.mina.core.polling.AbstractPollingIoProcessor.Processor 线程类,这个线程会在调用一个IoProcessor的方法public final void add(S session)的时候被启动(实际,在调用dispose()、add(S session)、remove(S session)这三个方法的时候,都会尝试着去启动一个Processor线程,如果没有启动则会启动这个线程),然后反复循环检测并处理队列中的写请求。
  4. 当IoProcessor关闭与一个IoSession实例实例相关的连接,则会将这个IoSession实例从 removingSessions队列中移除。
  5. 控制处理事件的通信量,主要是控制读写:如果没有注册读操作(SelectionKey.OP_READ),则注册一个,否则当一个 读操作已经就绪,则进行读数据的处理;如果没有注册写操作(SelectionKey.OP_WRITE),则注册一个,否则当一个写操 作已经就绪,则进行写数据的处理。
  6. 释放所有与IoProcessor有关的资源。

总结

经过上面的对比,我们已经能够知道IoHandler与IoProcessor的本质区别。
IoHandler是在IoFilterChain执行中最后一个IoFilter中被调用,比如,经过IoFilterChain进行 codec、logging等等操作之后,已经将通信层的数据转化成我们需要的业务对象数据,这时就可以调用我们定义的IoHandler实 现来进行处理。
IoProcessor是与实际的Socket或Channel相关的操作紧密相关的,也就是说,它是支撑Mina进行处理底层实际I/O请 求的处理器。

参考资源

Creative Commons License

本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、重新发布,但务必保留文章署名时延军(包含链接:http://shiyanjun.cn),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>