NIO框架之MINA源代码解析(二):mina核心引擎
NIO框架之MINA源代码解析(一):背景
MINA的底层还是利用了jdk提供了nio功能,mina仅仅是对nio进行封装。包含MINA用的线程池都是jdk直接提供的。
MINA的server端主要有accept、processor、session三部分组成的。当中accept主要负责在指定的port监听。若有新连接则建立一个新的session。processor则负责处理session相应的发送数据和接收数据并调用上层处理;session则缓存当前连接数据。
MINA採用了线程懒启动的技术,即最少启动线程。在MINA server启动的时候,仅仅有一个线程-accept,而且accept线程仅仅有一个,在指定的port进行监听(能够同一时候监听多个port,mina能够绑定多port)。
1、acceptor
先看下acceptor的主要类图吧。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hhb2ZhbndlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
mina server的启动入口是在NioSocketAcceptor.bind(InetSocketAddress)或者NioSocketAcceptor.bind(SocketAddress...)方法, acceptor.bind(new InetSocketAddress(1234));
然后会调用AbstractPollingIoAcceptor.bindInternal(List<? extends SocketAddress>)方法,在bindInternal方法里面会调用startupAcceptor()方法提交一个accept线程到线程池里面(仅仅提交一次),并初始化acceptor端的Selector。就这样一个acceptor线程启动了。
acceptor端业务相对简单,相当于在当前Selector里面监听acceptor事件,处理新连接并新建一个session放到相应的processor里面。
acceptor 代码。非常easy。
- private class Acceptor implements Runnable {
- 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();
- }
- }
- }
- }
2、processor
processor顾名思义,就是进行IO处理,处理当前session的数据读写,并进行业务处理。
在mina server初始化的时候,会初始化一个processor池,通过NioSocketAcceptor的构造器传入池的大小,默认是当前处理器的个数+1。
processor池里面有一个jdk提供的 线程池 - Executors.newCachedThreadPool()。各个processor线程会引用此线程池,即每一个processor线程都在这个线程池里面运行。
在mina server实际处理时,每一个processor相当于一个线程,轮流处理当前的session队列里面的数据(每一个processor里面的session相当于顺序处理,共享一个线程)。
每一个processor有一个Selector对象。
processor类图
processor端的处理逻辑相对有点复杂。看以下的流程图。
1、把新加入进来的session注冊到当前processor的Selector里面的read事件,并初始化session;
2、推断当前Selector是否有读写事件。
3、若第2步有读事件时。进入步骤4,若没有的话,直接到第6步。
4、处理当前读事件,并把处理后的数据放入到flush队列里面。
5、把第4步运行的结果flush到client;
6、处理session。比方session idle时间等。
7、又一次运行第1步,循环运行。
processor端代码。
- private class Processor implements Runnable {
- public void run() {
- assert (processorRef.get() == this);
- int nSessions = 0;
- lastIdleCheckTime = System.currentTimeMillis();
- for (;;) {
- try {
- // This select has a timeout so that we can manage
- // idle session when we get out of the select every
- // second. (note : this is a hack to avoid creating
- // a dedicated thread).
- long t0 = System.currentTimeMillis();
- int selected = select(SELECT_TIMEOUT);
- long t1 = System.currentTimeMillis();
- long delta = (t1 - t0);
- if ((selected == 0) && !wakeupCalled.get() && (delta < 100)) {
- // Last chance : the select() may have been
- // interrupted because we have had an closed channel.
- if (isBrokenConnection()) {
- LOG.warn("Broken connection");
- // we can reselect immediately
- // set back the flag to false
- wakeupCalled.getAndSet(false);
- continue;
- } else {
- LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
- // Ok, we are hit by the nasty epoll
- // spinning.
- // Basically, there is a race condition
- // which causes a closing file descriptor not to be
- // considered as available as a selected channel, but
- // it stopped the select. The next time we will
- // call select(), it will exit immediately for the same
- // reason, and do so forever, consuming 100%
- // CPU.
- // We have to destroy the selector, and
- // register all the socket on a new one.
- registerNewSelector();
- }
- // Set back the flag to false
- wakeupCalled.getAndSet(false);
- // and continue the loop
- continue;
- }
- // Manage newly created session first 处理新加入进来的session,并注冊到当前processor的Selector读事件
- nSessions += handleNewSessions();
- updateTrafficMask();
- // Now, if we have had some incoming or outgoing events,
- // deal with them
- if (selected > 0) {
- //LOG.debug("Processing ..."); // This log hurts one of the MDCFilter test...处理读事件。并把结果放入flush队列里面
- process();
- }
- // Write the pending requests 把flush队列里面的session的处理完的数据发送给client
- long currentTime = System.currentTimeMillis();
- flush(currentTime);
- // And manage removed sessions
- nSessions -= removeSessions();
- // Last, not least, send Idle events to the idle sessions
- notifyIdleSessions(currentTime);
- // Get a chance to exit the infinite loop if there are no
- // more sessions on this Processor
- if (nSessions == 0) {
- processorRef.set(null);
- if (newSessions.isEmpty() && isSelectorEmpty()) {
- // newSessions.add() precedes startupProcessor
- assert (processorRef.get() != this);
- break;
- }
- assert (processorRef.get() != this);
- if (!processorRef.compareAndSet(null, this)) {
- // startupProcessor won race, so must exit processor
- assert (processorRef.get() != this);
- break;
- }
- assert (processorRef.get() == this);
- }
- // Disconnect all sessions immediately if disposal has been
- // requested so that we exit this loop eventually.
- if (isDisposing()) {
- for (Iterator<S> i = allSessions(); i.hasNext();) {
- scheduleRemove(i.next());
- }
- wakeup();
- }
- } catch (ClosedSelectorException cse) {
- // If the selector has been closed, we can exit the loop
- break;
- } catch (Throwable t) {
- ExceptionMonitor.getInstance().exceptionCaught(t);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e1) {
- ExceptionMonitor.getInstance().exceptionCaught(e1);
- }
- }
- }
- try {
- synchronized (disposalLock) {
- if (disposing) {
- doDispose();
- }
- }
- } catch (Throwable t) {
- ExceptionMonitor.getInstance().exceptionCaught(t);
- } finally {
- disposalFuture.setValue(true);
- }
- }
- }
3、session
session做为一个连接的详细对象,缓存当前连接用户的一些信息。
session类图
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hhb2ZhbndlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
session对象是绑定在SelectableChannel的一个attach。
- class NioProcessor
- @Override
- protected void init(NioSession session) throws Exception {
- SelectableChannel ch = (SelectableChannel) session.getChannel();
- ch.configureBlocking(false);
- session.setSelectionKey(ch.register(selector, SelectionKey.OP_READ, session));//chanel 注冊读事件,并把session当做一个attach辅导SelectableChannel里面。
- }
NIO框架之MINA源代码解析(二):mina核心引擎的更多相关文章
- NIO框架之MINA源代码解析(一):背景
"你们的agent占了好多系统的port.把我们的非常多业务系统都给整死了,给我们造成了非常大的损失.要求你们的相关领导下周过来道歉" -- 来自我们的一个客户. ...
- (一)Mina源代码解析之总体架构
Apache Mina Server 是一个网络通信应用框架.也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也能够提供JAVA 对象的序列化服务.虚拟机管道通信服务等).M ...
- Volley 框架解析(二)--RequestQueue核心解读
主要围绕RequestQueue进行解读,它的两个请求队列CacheQueue.NetworkQueue是如何调用的,第一条请求的执行过程及如何处理重复请求?对RequestQueue及相关的类进行详 ...
- 【原创】NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
申明:本文由作者基于日常实践整理,希望对初次接触MINA.Netty的人有所启发.如需与作者交流,见文签名,互相学习. 学习交流 更多学习资料:点此进入 推荐 移动端即时通讯交流: 215891622 ...
- Spring源代码解析
Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...
- Spring源代码解析(收藏)
Spring源代码解析(收藏) Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...
- NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信
1.NIO超级陷阱 之所以说NIO超级陷阱,就是因为我在本系列开头的那句话,因为使用缺陷导致客户业务系统瘫痪.当然,我对这个问题进行了很深的追踪,包括对MINA源码的深入了解,但其实之所以会出现这个问 ...
- NIO框架Mina学习
前言: 找了篇文章看了看,nio框架数Mina用的最多! 代码: 服务端: package com.mina; import java.net.InetSocketAddress; import ja ...
- 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...
随机推荐
- SAS进阶《深入分析SAS》之数据汇总和展现
SAS进阶<深入分析SAS>之数据汇总和展现 1. 通过Print过程制作报表 proc print <data=数据集>; run; 选项: obs=修改观测序号列标签 no ...
- Python之数据聚合与分组运算
Python之数据聚合与分组运算 1. 关系型数据库方便对数据进行连接.过滤.转换和聚合. 2. Hadley Wickham创建了用于表示分组运算术语"split-apply-combin ...
- Android Studio Activity Intent 闪退崩溃 Toolbar
今天写登录注册页面,点击登录页面的“注册”按钮后软件突然崩溃,直接闪退,因为是新手,只能去网上搜.虽然网上解决方法众多,但也没找到可行的.想起来可以看Logcat,马上重新运行应用,查看崩溃时的日志, ...
- Mac上简单常用Terminal命令
方案1 SSH是一个非常伟大的工具,如果你要在互联网上远程连接到服务器,那么SSH无疑是最佳的候选.SSH是加密的,OpenSSH加密所有通信(包括密码),有效消除了窃听,连接劫持和其它攻击.本文将为 ...
- vba txt读写的几种方式
四种方式写txt 1.这种写出来的是ANSI格式的txt Dim TextExportFile As String TextExportFile = ThisWorkbook.Path & & ...
- 安全,轻松的Axios与Nuxt.js集成
modules: [ // Doc: https://github.com/nuxt-community/axios-module#usage '@nuxtjs/axios' ], /* ** Axi ...
- java_IO_装饰器
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 04C语言输入输出
C语言输入输出 输入字符getchar() #include <stdio.h> int main(){ putchar(getchar()); putchar(getchar()); ; ...
- 03Oracle Database 物理结构,逻辑结构
Oracle Database 物理结构,逻辑结构 Oracle Database 物理结构 数据文件 Oracle数据库有一个或多个物理的数据文件.数据库的数据文件包含全部数据库数据.逻辑数据物理地 ...
- java mysql prepareStatement模糊查询like使用注意
今天在使用mysql 的like语句是,发现prepareStatement的like语句和一般的=写法有一样. 当要使用prepareStatement的like查询时,按照一般写法,都会写成: S ...