mina学习笔记四:交互的核心IoSession

画了多年板子才搞懂:Allegro内电层负片连接,Thermal Relief和Anti-Pad避坑全解析 本文深入解析Allegro内电层设计中Thermal Relief与Anti-Pad的关键技术要点,帮助工程师避免常见设计陷阱。通过六层板实战案例,详细阐述负片连接逻辑、热风焊盘参数设计及隔离盘优化策略,提升PCB设计的可靠性与EMI性能。 阅读详情

    这一章讲的就是IoSession,session这个词做web应用的人应该都是耳熟能详,在mina中IoSession也是起着相同的作用。mina为每个客户端提供了会话session,session是服务器端和客户端的连接持有者,直到连接中断session才会被销毁。

    IoSession不仅是connection的相关信息,同时,服务器端还能通过他存储其他可能需要的数据信息。好,让我们一步一步剖析IoSession那不平凡的一生吧。

1. IoSession在服务器端的创建第一步:accept(processor,handle)

    接着上节说,我们已经知道,NioSocketAcceptor.bind()后,最终会生成一个在自身维护的executor线程池内提交的任务Acceptor(单一线程),该任务不断的轮询判断服务是否关闭、同时等待新的连接接入。当有新的连接接入时:
Acceptor.run(){
	...
	//selectedHandles()获取一个selector中注册的服务监听的serversocketchannel的迭代器
	processHandles(selectedHandles());
	...
}
    创建session、并为其分配指定IoProcessor的秘密都在AbstractPollingIoAcceptor.processHandles()这个方法中。我们接着看:
	private void processHandles(Iterator<ServerSocketChannel> handles) throws Exception {
            while (handles.hasNext()) {
                H handle = handles.next();
                handles.remove();

                // 创建session并将本地的processor处理池当参数传递给session
		// 注意这里还未分配具体的processor
                S session = accept(processor, handle);

                if (session == null) {
                    continue;
                }
		// 初始化Session
                initSession(session, null, null);

                // 将session分配给具体的processor
                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) {
	//AbstractIoSession(IoService service)
        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;

        //在关闭时的动作,下面以此展开对IoFuture的探讨。
        closeFuture.addListener(SCHEDULED_COUNTER_RESETTER);

        //为session创建唯一的ID
        sessionId = idGenerator.incrementAndGet();
	//END AbstractIoSession(IoService service)

	//NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel)
	//即当前session的SocketChannel
	this.channel = channel;
	//这是ioservice中的processor池哦
        this.processor = processor;
	//这步过滤器链厉害了,后面细说
        filterChain = new DefaultIoFilterChain(this);
	//END NioSession(IoProcessor<NioSession> processor, IoService service, Channel channel)

	//设置各种初始参数
        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() {
        // Only read / write / connect / write future can cause dead lock. 
        if (!(this instanceof CloseFuture || this instanceof WriteFuture || this instanceof ReadFuture || this instanceof ConnectFuture)) {
            return;
        }

        // Get the current thread stackTrace. 
        // Using Thread.currentThread().getStackTrace() is the best solution,
        // even if slightly less efficient than doing a new Exception().getStackTrace(),
        // as internally, it does exactly the same thing. The advantage of using
        // this solution is that we may benefit some improvement with some
        // future versions of Java.
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        // Simple and quick check.
        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.");
            }
        }

        // And then more precisely.
        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) {
                // Ignore
            }
        }
    }

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)在下面段代码,创建(若没有)并返回Processor
	//第二步在该Processor中add(session),这里就是调用AbstractPollingIoProcessor.add(),后面有更多描述
        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.");
        }

        // 将session添加到ConcurrentLinkedQueue的队列中,并启动Processor 的worker线程
        newSessions.add(session);
 	//启动Processor线程
	startupProcessor(); 
    }

    我们很有必要具体看一下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);

                    ...
                    // 注册新的session连接,并初始化session信息,如过滤器链,
		    // 通知service的监听器
                    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()做了如下工作:
	//在selector上注册感兴趣的相应集合读操作
        SelectableChannel ch = (SelectableChannel) session.getChannel();
        ch.configureBlocking(false);
        session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));

	//将service的过滤器链设置在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等,我们接下来几章继续探讨。
图文详解mina框架 Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型。Mina 主要有1.x 和... 阅读详情

相关推荐

032、准谐振反激变换器(QR-Flyback)原理

文章摘要: 本文以65W PD快充炸管案例切入,解析准谐振反激变换器(QR-Flyback)原理。通过主动利用漏感与MOS结电容的谐振,在Vds波谷开通实现近似ZVS,显著降低开关损耗与电压应力。对比传统反激,QR模式轻载效率提升8%,Vds尖峰减少23%。关键设计要点包括:变压器漏感需<3%、谷底检测电路需抗干扰、启动阶段需防过冲。推荐NCP1340等专用控制芯片,并强调VCC电容优选低ESR陶瓷材质。QR适用于30W-150W场景,其核心优势在于轻载效率优化,但需平衡漏感控制与可靠性。作者结合实测数据与

Jack010169的博客 53

七-Mina学习之IOSession接口

1.IOSession    主要是网络通信双方建立连接之间的描述。    作用:可以完成对于连接的一些管理,可以发送或者读取数据,并且可以设置会话的上下文信息。2.SessionConfig    提供我们对连接的配置信息的描述。可以设置读写缓冲区,读和写的空闲时间,设置读写超时信息。3.API    IOSesson:        write(Object message);发送数据    ...

学而不思则罔,思而不学则怠 4003

MNIST手写数字数据集+7000张图片.rar

1、MNIST手写数字数据集,7000张高质量图片数据,图片格式为jpg,分为测试集和训练集。2、数据集详情展示和更多数据集下载:https://blog.csdn.net/m0_64879847/article/details/132301975

IoSession

IoSession 代表两个端口之间无视传输类型的连接的处理类 提供用户定义的参数,用户定义参数是会话相关的应用特定数据。它通常包含代表高等级协议状态的对象也变成在filters和handlers之间交换数据的一种方式   &lt;调整传输类型的具体属性&gt; 你可以简单向下转型session对象成一个合适的子类   &lt;线程安全&gt; IoSession是线程安全的,...

lolichan的博客 1066

Mina基础():理解IoSession、I/O Processor、IoBuffer

Session会话                Session会话处于 MINA核心位置:每当一个客户端连接到服务器,一个新的会话会被创建,并会在客户端关掉连接前一直保存在内存中。会话用于保存连接的持久信息,以及在请求处理过程中、会话的生命周期中服务器可能需要用到的任何信息。 IoSession:主要描述我们的网络通信双方所建立的连接之间的描述。     作用:可以完成一些对于连接...

矢落叶の博客 1941

Mina框架IoSession详解

通过Mina官 网文档,我们可以看到,有如下几个状态: Connected : the session has been created and is available Idle : the session hasn’t processed any request for at least a period of time (this period ...

weixin_33978044的博客 1330

Mina学习之IoSession

Session(会话)是Mina核心部分:每当一个clinent连接到server时,都会创建一个新的session,并且保存在内存中知道该链接断开。 session 是用来存储一些关于连接信息,加上各种服务端在处理请求时可能会用到的信息。 Session state session有一个状态,并且会随着时间变化。 1. Connected:session已经被创建并且是...

weixin_30391339的博客 694

Mina源码阅读笔记(五)—Mina对连接的操作IoSession

mina

smithdoudou88的专栏 5242

mina - IoSession

mina

wjy2460956668的博客 737

MINA核心结构和处理消息的逻辑流程

1.MINA 核心结构 IoService 最底层的是IOService,负责具体的IO相关工作。这一层的典型代表有IOSocketAcceptor和IOSocketChannel,分别对应TCP协议下的服务端和客户端的IOService。IOService的意义在于隐藏底层IO的细节,对上提供统一的基于事件的异步IO接口。每当有数据到达时,IOService会先调用底层IO接口读取数...

mingchengtx的专栏 454

Mina源码解析之IoSession

本篇文章主要剖析IoSession的在mina中的作用还有粘包的问题 在Mina中所有的连接都被封装成了IoSession(关于Mina是如何将连接封装成IoSession的,请参见Mina源码解析之IoService),

Mr孔先森 5954

mina源码学习之IoSession

Acceptor线程接收到客户端的请求之后就会调用processHandles方法处理请求。 private void processHandles(Iterator<H> handles) throws Exception { while (handles.hasNext()) { H handle = handles.next();

程旭元的博客 938

Mina之session

1、IoSession与底层的传输层类型无关,表示通信双端的连接。提供用户自定义属性,可以用于在过滤器和处理器之间交换用户自定义协议相关信息。每个会话都由一个Service来提供服务,同时有一个Handler负责此会话的I/O事件处理。最重要的两个方法就是read和write,这两个方法都是异步执行,如要真正完成必须在其结果上进行等待。关闭会话的方法close也是异步执行的,也就是应等待返回的Cl...

weixin_30502157的博客 379

Mina IoSession所扮演的角色说明

原文地址:Mina session(Apache user guide Chapter4 session) Session会话是Mina核心:每次客户端连接到服务器,创建一个新的session会话,并将保存在内存中,直到客户端断开连接。Session会话连接,用于存储持久化信息,并且服务器可能需要处理任何类型的信息在请求期间,并最终在整个会话期间存活。 1.1.1. Session状态

纸上得来终觉浅,绝知此事要躬行 2680

mina学习笔记五:做嫁衣的IoFilter和IoListener

总有那么一群人,他们默默无闻的工作在自己的岗位上,你永远都接触不到他们,可是离了他们

Yoara的备忘录 8721
上一篇: mina学习笔记三:一切的源头IoService
下一篇: mina学习笔记五:做嫁衣的IoFilter和IoListener
Yoara
博客等级 码龄18年 79粉丝 93原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值