简介

session类图

Mina每建立一个连接同时会创建一个session对象,用于保存这次读写需要用到的所有信息。从抽象类AbstractIoSession中可以看出session具有如下功能:
1、从attributes成员可以看出session可以存放用户关心的键值对
2、注意到WriteRequestQueue,这是一个写请求队列,processor中调用flush或者flushNow方法时会将用户写入的数据包装成一个writeRequest对象,并加入这个队列中。
3、提供了大量的统计功能,比如接收到了多少消息、最后读取时间等
在代码中一般是这样使用session的
// 创建服务器监听
IoAcceptor acceptor = new NioSocketAcceptor();
// 设置buffer的长度
acceptor.getSessionConfig().setReadBufferSize(2048);
// 设置连接超时时间
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

session做为一个连接的具体对象,缓存当前连接用户的一些信息。

创建好acceptor或者connector之后,通过IoSessionConfig对session对行配置。
用得最多的是通过session写入数据,这是调用了IoSession的write方法
WriteFuture write(Object message);
WriteFuture write(Object message, SocketAddress destination);

下面着重分析创建过程以及session的状态

创建与初始化

每建立一个连接,就会创建一个session,IoAcceptor的accept方法的返回值正是一个session。见NioSocketAcceptor.accept(IoProcessor<NioSession> processor, ServerSocketChannel handle)方法:

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

        SelectionKey key = handle.keyFor(selector);

        if ((key == null) || (!key.isValid()) || (!key.isAcceptable())) {
return null;
} // accept the connection from the client
SocketChannel ch = handle.accept(); if (ch == null) {
return null;
} return new NioSocketSession(this, processor, ch);
}
由以上代码可知,session包含了对众多对象的引用,比如processor,socketChannel,SelectionKey,IoFilter等。
session在创建好后,紧接着就会对其进行初始化。AbstractIoService.initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer)方法如下:

    protected final void initSession(IoSession session, IoFuture future, IoSessionInitializer sessionInitializer) {
// Update lastIoTime if needed.
if (stats.getLastReadTime() == 0) {
stats.setLastReadTime(getActivationTime());
} if (stats.getLastWriteTime() == 0) {
stats.setLastWriteTime(getActivationTime());
} // Every property but attributeMap should be set now.
// Now initialize the attributeMap. The reason why we initialize
// the attributeMap at last is to make sure all session properties
// such as remoteAddress are provided to IoSessionDataStructureFactory.
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);
} if ((future != null) && (future instanceof ConnectFuture)) {
// DefaultIoFilterChain will notify the future. (We support ConnectFuture only for now).
session.setAttribute(DefaultIoFilterChain.SESSION_CREATED_FUTURE, future);
} if (sessionInitializer != null) {
sessionInitializer.initializeSession(session, future);
} finishSessionInitialization0(session, future);
}
设置上次读写时间,初始化属性map和写请求队列等。
session被初始化好之后会加入到processor中,processor中有一个队列专门存放session:
例如:AbstractPollingIoProcessor.java中有:

private final Queue<S> newSessions = new ConcurrentLinkedQueue<S>();

    /** A queue used to store the sessions to be removed */
private final Queue<S> removingSessions = new ConcurrentLinkedQueue<S>(); /** A queue used to store the sessions to be flushed */
private final Queue<S> flushingSessions = new ConcurrentLinkedQueue<S>(); /**
* A queue used to store the sessions which have a trafficControl to be
* updated
*/
private final Queue<S> trafficControllingSessions = new ConcurrentLinkedQueue<S>(); /** The processor thread : it handles the incoming messages */
private final AtomicReference<Processor> processorRef = new AtomicReference<Processor>();
加入队列之后,processor就会从队列中取出session,以下是processor的run方法关键代码:
  1. private class Processor implements Runnable {
    public void run() {
    for (;;) {
    long t0 = System.currentTimeMillis();
    int selected = select(SELECT_TIMEOUT);
    long t1 = System.currentTimeMillis();
    long delta = (t1 - t0); if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {
    if (isBrokenConnection()) {
    wakeupCalled.getAndSet(false);
    continue;
    } else {
    registerNewSelector();
    }
    wakeupCalled.getAndSet(false);
    continue;
    } nSessions += handleNewSessions(); updateTrafficMask(); if (selected > 0) {
    process();
    } nSessions -= removeSessions(); }
    }
    }

1、不断地调用select方法来检查是否有session准备就绪,如果没有或者间隔时间小于100ms则检查selector是否可用,如果不可用重新建一个selector(这里linux下的epoll的问题可能导致selector不可用。)

2、从newSessions队列中取出这些session,并将其负责的通道注册到selector上
3、处理准备就绪的session
AbstractPollingIoProcessor.java

private void process(S session) {
// Process Reads
if (isReadable(session) && !session.isReadSuspended()) {
read(session);
} // Process writes
if (isWritable(session) && !session.isWriteSuspended()) {
// add the session to the queue, if it's not already there
if (session.setScheduledForFlush(true)) {
flushingSessions.add(session);
}
}
}
总结一下创建与初始化过程:连接到来创建一个session,初始化好之后加入到processor负责的一个队列中。processor线程会把队列中的session对应的通道都注册到它自己的selector上,然后这个selector轮询这些通道是否准备就绪,一旦准备就绪就调用对应方法进行处理(read or flushNow)。

状态

Mina中的session具有状态,且状态之间是可以相互转化的
Connected:session被创建时处于这种状态
Idle:没有请求可以处理(可配置)
Closing:正处于关闭状态(可能正在做一些清理工作)
Closed:关闭状态
下图是这几种状态之间的转化图:

IoFilter与IoHandler就是在这些状态上面加以干预,下面重点看一下IDLE状态,它分三种:

Idle for read:在规定时间内没有数据可读
Idle for write:在规定时间内没有数据可写
Idle for both:在规定时间内没有数据可读和可写
这三种状态分别对应IdleStatus类的三个常量:READER_IDLE、WRITER_IDLE、BOTH_IDLE
前面session的用法中有如下设置:

acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); 
acceptor的run方法中调用了notifyIdleSessions

每隔一秒一检查是否到达了设置的空闲时间

  1. private static void notifyIdleSession0(IoSession session, long currentTime, long idleTime, IdleStatus status,
    long lastIoTime) {
    if ((idleTime > 0) && (lastIoTime != 0) && (currentTime - lastIoTime >= idleTime)) {
    session.getFilterChain().fireSessionIdle(status);
    }
    }

如果当前时间减去上一次IDLE事件触发的时间大于用户设置的idleTime,则触发一次sessionIdle事件。

public void fireSessionIdle(IdleStatus status) {
session.increaseIdleCount(status, System.currentTimeMillis());
Entry head = this.head;
callNextSessionIdle(head, session, status);
}

increaseIdleCount这个方法会更新lastToTime的值为当前时间,紧接着穿透过滤器链(当然在过滤器的sessionIdle中可能会做一些操作)到达IoHandler的sessionIdle方法,如果需要在session空闲的时候做一些操作,就可以在这个方法里面做。

 
 

mina2中的session的更多相关文章

  1. 在 ASP.NET CORE 中使用 SESSION

    Session 是保存用户和 Web 应用的会话状态的一种方法,ASP.NET Core 提供了一个用于管理会话状态的中间件.在本文中我将会简单介绍一下 ASP.NET Core 中的 Session ...

  2. Tomcat中的Session小结

    什么是Session 对Tomcat而言,Session是一块在服务器开辟的内存空间,其存储结构为ConcurrentHashMap: Session的目的 Http协议是一种无状态协议,即每次服务端 ...

  3. .ashx中使用Session

    在一般处理程序中给session赋值是报错:未将对象引用设置到对象的实例.

  4. strust2中使用session

    在Struts2里,如果需要在Action中使用session,可以通过下面两种方式得到1.通过ActionContext class中的方法getSession得到2.Action实现org.apa ...

  5. 在IHttpHandler中获取session

    因为业务要异步通过IHttpHandler获得数据,但还要根据当前登录人员的session过滤,因此要在在IHttpHandler中获取session 方法是HttpHandler容器中如果需要访问S ...

  6. Java中对session的简单操作

    1.jsp中操作session <% String name=(String)request.getSession().getAttribute("username"); / ...

  7. ASP.NET中的Session怎么正确使用

    Session对象用于存储从一个用户开始访问某个特定的aspx的页面起,到用户离开为止,特定的用户会话所需要的信息.用户在应用程序的页面切换时,Session对象的变量不会被清除. 对于一个Web应用 ...

  8. 如何在报表权限中使用session

    1. 问题描述 权限中使用session,一般是用来存放用户名和密码,下面以报表开发工具FineReport为例,分两种情况介绍用户名和密码的保存: 2. 同一应用下session 由于session ...

  9. [转]tomcat中的session管理

    转载地址:http://blog.csdn.net/iloveqing/article/details/1544958 当一个sesson开始时,Servlet容器会创建一个HttpSession对象 ...

随机推荐

  1. python性能分析——insert()

    我们在list中插入数据时,经常使用这两个函数: append():在列表的末尾增加一个数据 insert():在某个特定位置前加一个数据 Python内的list实现是通过数组实现的,而不是链表的形 ...

  2. 【HDOJ4635】【Tarjan缩点+思维】【经典】

    http://acm.hdu.edu.cn/showproblem.php?pid=4635 Strongly connected Time Limit: 2000/1000 MS (Java/Oth ...

  3. hdu6441 Find Integer (费马大定理)

    #include<bits/stdc++.h> using namespace std; int main() { int T; scanf("%d",&T); ...

  4. debian 配置linuxptp 软件时间戳

    编程之路刚刚开始,错误难免,希望大家能够指出. ntp,ptp,ntp,ptp 本文只说软件时间戳 先上几个推荐的网址,可以更好的了解ptp: https://docs.fedoraproject.o ...

  5. AangularJS的表单验证

    Angular能够将HTML5表单验证功能同它自己的验证指令结合起来使用 Angular提供了很多表单验证指令: 1. 必填项:验证表单输入是否填写,只需在html标签上标记required   如: ...

  6. 【HAOI2008】圆上的整点

    数学题 原题:平面上有一个圆, 圆心坐标为(0,0),半径为n. 问圆周上有多少个整点. 整点的定义即x,y坐标均为整数的点. 这根本就是一道数学题,注意是数学题,不是数论,数学! 纯粹就看魔性变公式 ...

  7. linux之shell终端使用操作快捷键

    所谓的shell终端就是桌面右键里面的打开终端那个终端 敲命令是一件很有趣的事,可是有时候我们会遇到一些很麻烦的事 例如,命令太长导致敲完后一大串字符可读性低,想把vi filename 快速改为ca ...

  8. scala学习笔记-面向对象编程之Trait

    将trait作为接口使用 1 // Scala中的Triat是一种特殊的概念 2 // 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 3 // 在triat中可 ...

  9. openstack--1--基础环境搭建

    Openstack介绍 OpenStack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目. Openstack包含三大项 ...

  10. js 递归

    我理解的递归就是自己调用自己,也就是函数在调用的时候会形成 call stack 调用堆栈.这些数据是用来函数调用完成后,回复之前的函数环境或者局部变量之类的,一般这个都有大小限制,不可能无限生成函数 ...