这一章讲的就是IoSession,session这个词做web应用的人应该都是耳熟能详,在mina中IoSession也是起着相同的作用。mina为每个客户端提供了会话session,session是服务器端和客户端的连接持有者,直到连接中断session才会被销毁。
IoSession不仅是connection的相关信息,同时,服务器端还能通过他存储其他可能需要的数据信息。好,让我们一步一步剖析IoSession那不平凡的一生吧。
1. IoSession在服务器端的创建第一步:accept(processor,handle)
接着上节说,我们已经知道,NioSocketAcceptor.bind()后,最终会生成一个在自身维护的executor线程池内提交的任务Acceptor(单一线程),该任务不断的轮询判断服务是否关闭、同时等待新的连接接入。当有新的连接接入时:
- Acceptor.run(){
- ...
-
- processHandles(selectedHandles());
- ...
- }
创建session、并为其分配指定IoProcessor的秘密都在AbstractPollingIoAcceptor.processHandles()这个方法中。我们接着看:
- private void processHandles(Iterator<ServerSocketChannel> handles) throws Exception {
- while (handles.hasNext()) {
- H handle = handles.next();
- handles.remove();
-
-
-
- S session = accept(processor, handle);
-
- if (session == null) {
- continue;
- }
-
- initSession(session, null, null);
-
-
- session.getProcessor().add(session);
- }
- }
第一步accept(processor,handle)方法,调用子类实现的NioSession accept(...)方法,判断了SelectionKey的有效性后,获得连接的通道SocketChannel ch = handle.accept();最后以此为传参创建IoSession对象return new NioSocketSession(this, processor, ch);我们看下构造函数做了什么(为了方便起见,相关的父类构造函数都写在一块了):
- public NioSocketSession(IoService service, IoProcessor<NioSession> processor, SocketChannel channel) {
-
- this.service = service;
- this.handler = service.getHandler();
-
-
- long currentTime = System.currentTimeMillis();
- creationTime = currentTime;
- lastThroughputCalculationTime = currentTime;
- lastReadTime = currentTime;
- lastWriteTime = currentTime;
- lastIdleTimeForBoth = currentTime;
- lastIdleTimeForRead = currentTime;
- lastIdleTimeForWrite = currentTime;
-
-
- closeFuture.addListener(SCHEDULED_COUNTER_RESETTER);
-
-
- sessionId = idGenerator.incrementAndGet();
-
-
-
-
- this.channel = channel;
-
- this.processor = processor;
-
- filterChain = new DefaultIoFilterChain(this);
-
-
-
- config = new SessionConfigImpl();
- this.config.setAll(service.getSessionConfig());
- }
右边这张图是IoSession的类图结构,比较清晰。
我们细细的分析一下NioSocketSession在初始化时做了哪些工作,首先持有了sevice及其处理类引用,并设置了一些初始时间信息,然后他在关闭时注册了监听操作closeFuture.addListener(SCHEDULED_COUNTER_RESETTER)。
1.1 IoFuture
我们已经很多次看到IoFuture,有必要来详细了解一下IoFuture的作用。那用过Executor的同学都知道,submit()提交Callback的任务后将返回一个Future表示这个任务的操作结果,这里的IoFuture的作用是类似的,他代表一次相关于IoSession的操作的操作结果,同时,还可以在他完成时获取定义相关的操作。我们看下他的子类结构图
图4.1 IoFuture子类结构图
由图中可见,对session相关的操作例如关闭、连接、写、读等操作,IoFuture全都有子类去支持。session的操作可以很方便的通过IoFuture查看处理的结果。我们看下IoFuture接口的API清单:
- addListener(IoFutureListener<?>)
- await()
- await(long)
- await(long, TimeUnit)
- awaitUninterruptibly()
- awaitUninterruptibly(long)
- awaitUninterruptibly(long, TimeUnit)
- getSession()
- isDone()
- removeListener(IoFutureListener<?>)
从清单我们了解到:1.IoFuture支持添加操作完成的监听器,2.IoFuture支持可中断、可设置超时时间的阻塞式操作。3.isDone()则返回操作的完成状态。
- public interface IoFutureListener<F extends IoFuture> extends EventListener {
- void operationComplete(F future);
- }
在他的直接实现子类DefaultIoFuture里面,我们看一下该类很主要一个实现方法setValue(Object newValue),所有其他子类都会调用该方法以完成具体的操作:
- public void setValue(Object newValue) {
- synchronized (lock) {
-
- if (ready) {
- return;
- }
-
- result = newValue;
-
- ready = true;
-
- if (waiters > 0) {
- lock.notifyAll();
- }
- }
- 知监听器执行完成操作
- notifyListeners();
- }
DefaultIoFuture中个还有一个检查死锁的方法,大家有兴趣可以看下是怎么判断死锁的:
- private void checkDeadLock() {
-
- if (!(this instanceof CloseFuture || this instanceof WriteFuture || this instanceof ReadFuture || this instanceof ConnectFuture)) {
- return;
- }
-
-
-
-
-
-
-
- StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
-
-
- for (StackTraceElement s : stackTrace) {
- if (AbstractPollingIoProcessor.class.getName().equals(s.getClassName())) {
- IllegalStateException e = new IllegalStateException("t");
- e.getStackTrace();
- throw new IllegalStateException("DEAD LOCK: " + IoFuture.class.getSimpleName()
- + ".await() was invoked from an I/O processor thread. " + "Please use "
- + IoFutureListener.class.getSimpleName() + " or configure a proper thread model alternatively.");
- }
- }
-
-
- for (StackTraceElement s : stackTrace) {
- try {
- Class<?> cls = DefaultIoFuture.class.getClassLoader().loadClass(s.getClassName());
- if (IoProcessor.class.isAssignableFrom(cls)) {
- throw new IllegalStateException("DEAD LOCK: " + IoFuture.class.getSimpleName()
- + ".await() was invoked from an I/O processor thread. " + "Please use "
- + IoFutureListener.class.getSimpleName()
- + " or configure a proper thread model alternatively.");
- }
- } catch (Exception cnfe) {
-
- }
- }
- }
1.2 IoSession的过滤器链
IoSession是有状态的,它包括:
- Connected :IoSession已被创建且生效。
- Idle :会话在一定时间内没有处理任何请求,它包括下面三个子状态:
- Idle for read : 一段时间内没有接受数据。
- Idle for write : 一段时间内没有发送数据。
- Idle for both : 一段时间内没有产生接受或发送数据的操作。
- Closing : IoSession正在关闭中。
- Closed : session已经被关闭。
图4.2 IoSession状态变化图
每个状态都会引起一系列的过滤器的对应的操作,从这句代码中我们了解到,每个IoSession都有他自己的过滤器链,即我们可以为每个session定制过滤器链。
- filterChain = new DefaultIoFilterChain(this);
IoSession会先继承IoService的过滤器链,过滤器链将放在下一节统一分析。
2. IoSession在服务器端的创建第二步:initSession(session, null, null)
这一步initSession(session, null, null);主要的有两段可配置的属性,一个就是存储attribute属性键值对的Map,默认是ConcurrentHashMap;另一个就是发送队列,默认是ConcurrentLinkedQueue。若要实现自己的数据结构,需要重写接口IoSessionDataStructureFactory或继承DefaultIoSessionDataStructureFactory。
- try {
- ((AbstractIoSession) session).setAttributeMap(session.getService().getSessionDataStructureFactory()
- .getAttributeMap(session));
- } catch (IoSessionInitializationException e) {
- throw e;
- } catch (Exception e) {
- throw new IoSessionInitializationException("Failed to initialize an attributeMap.", e);
- }
-
- try {
- ((AbstractIoSession) session).setWriteRequestQueue(session.getService().getSessionDataStructureFactory()
- .getWriteRequestQueue(session));
- } catch (IoSessionInitializationException e) {
- throw e;
- } catch (Exception e) {
- throw new IoSessionInitializationException("Failed to initialize a writeRequestQueue.", e);
- }
3. IoSession在服务器端的创建第三步:session.getProcessor().add(session);
这一步就是为我们新生成的IoSession在IoService的Processor池中指定一个Processor处理。分配的方式就是取模分配,完成后在分配Processor中增加该IoSession的引用。SimpleIoProcessorPool中的代码如下:
- public final void add(S session) {
-
-
- getProcessor(session).add(session);
- }
- private IoProcessor<S> getProcessor(S session) {
- IoProcessor<S> processor = (IoProcessor<S>) session.getAttribute(PROCESSOR);
-
- if (processor == null) {
- if (disposed || disposing) {
- throw new IllegalStateException("A disposed processor cannot be accessed.");
- }
-
- processor = pool[Math.abs((int) session.getId()) % pool.length];
-
- if (processor == null) {
- throw new IllegalStateException("A disposed processor cannot be accessed.");
- }
-
- session.setAttributeIfAbsent(PROCESSOR, processor);
- }
-
- return processor;
- }
4. SimpleIoProcessorPool、IoProcess和NioSocketAcceptor之间的关系
上节我们留了个悬念,SimpleIoProcessorPool、IoProcess和IoServices之间的到底是关系呢?我们从SimpleIoProcessorPool初始化池说起:
- for (int i = 1; i < pool.length; i++) {
- ...
- pool[i] = processorConstructor.newInstance(this.executor);
- ...
- }
这段代码将生成的线程池当做构造传参传递给所有的NioProcess,即数组池中所有NioProcess都是共享一个Executro线程池的(newCashedThreadPool)。AbstractPollingIoProcessor.add()方法做了这些工作:
- public final void add(S session) {
- if (disposed || disposing) {
- throw new IllegalStateException("Already disposed.");
- }
-
-
- newSessions.add(session);
-
- tartupProcessor();
- }
我们很有必要具体看一下Processor线程做了什么:
- private class Processor implements Runnable {
- public void run() {
- ...
- for (;;) {
- try {
-
- long t0 = System.currentTimeMillis();
- int selected = select(1000);
- long t1 = System.currentTimeMillis();
- long delta = (t1 - t0);
-
- ...
-
-
- nSessions += handleNewSessions();
- ...
-
- if (selected > 0) {
- process();
- }
- ...
-
- nSessions -= removeSessions();
-
-
- notifyIdleSessions(currentTime);
-
- ...
-
- if (isDisposing()) {
- for (Iterator<S> i = allSessions(); i.hasNext();) {
- scheduleRemove(i.next());
- }
-
- wakeup();
- }
- } catch ...
- }
- ...
- }
- }
其中,handlesNewSessions()做了如下工作:
-
- SelectableChannel ch = (SelectableChannel) session.getChannel();
- ch.configureBlocking(false);
- session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));
-
-
- IoFilterChainBuilder chainBuilder = session.getService().getFilterChainBuilder();
- chainBuilder.buildFilterChain(session.getFilterChain());
-
- IoServiceListenerSupport listeners = ((AbstractIoService) session.getService()).getListeners();
- listeners.fireSessionCreated(session);
至此SimpleIoProcessorPool、IoProcess和IoServices的关系就清晰了。
- SimpleIoProcessorPool维护一组IoProcess[] ,每个IoProcess都在线程池中绑定了工作线程,该工作线程开启一个selector并监听session交互OP_READ。
- public NioProcessor(Executor executor) {
- super(executor);
-
- try {
- selector = Selector.open();
- } catch (IOException e) {
- throw new RuntimeIoException("Failed to open a selector.", e);
- }
- }
- NioSocketAcceptor持有一个pool的引用
- NioSocketAcceptor自身也有个单线程的executor,用于监听新的session的链接OP_ACCEPT。
- NioSocketAcceptor.init() throws Exception {
- selector = Selector.open();
- }
结语
今天分析了IoSession后,mina的框架体系就比较清晰了,不过还有重要的filter、handler等,我们接下来几章继续探讨。