(二)Mina源码解析之IoService

来源:互联网 发布:天猫数据分析 编辑:程序博客网 时间:2024/05/16 12:17

在mina中,IoService提供基础的I/O服务、管理IoSession,它是Mina框架中最重要的一部分,大部分基础的I/O操作都是在IoService的实现类中被处理的

图一

IoService有如下几个功能:

1、通信管理:在server端和client端处理数据的传输

2、管理过滤器链:管理过滤器链

3、调用Handler:当有新消息到来时调用Handler

4、统计管理:更新发送的消息数,发送的字节数等等

5、listeners管理:管理监听器

6、管理sessions:创建和删除session,空闲检测

server端IoService的类结构如下:

图二

通信管理

mina的工作原理如下:

图三

在服务端,bind一个端口后,会创建一个Acceptor线程负责监听是否有新的连接到来,新的连接到来之后封装成IoSession交给Processor线程来处理,Processor线程会监控是否有数据从已经建立的连接中传输到server并进行处理


一个简单的TCPServer如下所示:

public class MinaServer {    private static Logger logger = LoggerFactory.getLogger(MinaServer.class);    public static void main(String[] args) {        NioSocketAcceptor acceptor = new NioSocketAcceptor();   //1        acceptor.getSessionConfig().setBothIdleTime(10000);     //2        acceptor.getSessionConfig().setMaxReadBufferSize(2048); //3        acceptor.setHandler(new ServerHandler());               //4        acceptor.getFilterChain().addLast("codec",              //5                new ProtocolCodecFilter(                        new TextLineCodecFactory(                                Charset.forName("UTF-8"),                                LineDelimiter.WINDOWS.getValue(),                                LineDelimiter.WINDOWS.getValue())));        try {            acceptor.bind(new InetSocketAddress("127.0.0.1", 8866));//6        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        logger.debug("MinaServer 启动 {}","success");    }}
本篇文章主要讲解上述代码中的第1步和第6步,mina都做了哪些工作,先用一张顺序图来说明mina在第1步和第6步做了哪些工作,顺序图如下:


图四

首先创建一个NioSocketAcceptor对象

NioSocketAcceptor acceptor = new NioSocketAcceptor();   //1
在创建NioSocketAcceptor对象的时候先创建一个父类对象AbstractPollingIoAcceptor并传入两个参数,第一个参数是一个DefaultSocketSessionConfig类,mina所有的可配置信息都在这个对象中,我们通过acceptor.getSessionConfig()方法来进行配置,getSessionConfig()方法获得的对象就是现在创建的这个DefaultSocketSessionConfig对象。
public NioSocketAcceptor() {        super(new DefaultSocketSessionConfig(), NioProcessor.class);        ((DefaultSocketSessionConfig) getSessionConfig()).init(this);    }

创建父类对象时用到了子类对象NioSocketAcceptor传进来的NioProcess.class参数,该参数是一个Class对象,AbstractPollingIoAcceptor根据该参数创建一个SimpleIoProcessorPool对象

protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Class<? extends IoProcessor<S>> processorClass) {        this(sessionConfig, null, new SimpleIoProcessorPool<S>(processorClass), true, null);    }

SimpleIoProcess是IoProcessor的实现类,该类的主要功能是创建一个IoProcessor数组,该数组的大小为所在机器的处理器数量+1,如代码1处所示,在代码2处通过反射创建IoProcessor实例,在代码2以下又创建了固定数量的IoProcessor实例。

processor线程主要负责具体的IO读写操作和IoFilterChain、IoHandler的逻辑,建立多个processor可以充分利用多核的处理能力,图三中的多个processor就是在这里创建

public SimpleIoProcessorPool(Class<? extends IoProcessor<S>> processorType, Executor executor, int size,             SelectorProvider selectorProvider) {        if (processorType == null) {            throw new IllegalArgumentException("processorType");        }        if (size <= 0) {            throw new IllegalArgumentException("size: " + size + " (expected: positive integer)");        }        // Create the executor if none is provided        createdExecutor = (executor == null);        if (createdExecutor) {            this.executor = Executors.newCachedThreadPool();            // Set a default reject handler            ((ThreadPoolExecutor) this.executor).setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        } else {            this.executor = executor;        }        pool = new IoProcessor[size]; //1)创建IoProcess数组        boolean success = false;        Constructor<? extends IoProcessor<S>> processorConstructor = null;        boolean usesExecutorArg = true;        try {            // We create at least one processor            try {                try {                    processorConstructor = processorType.getConstructor(ExecutorService.class);//2) 创建IoProcessor                    pool[0] = processorConstructor.newInstance(this.executor);                } catch (NoSuchMethodException e1) {                    // To the next step...                    try {                        if(selectorProvider==null) {                            processorConstructor = processorType.getConstructor(Executor.class);                            pool[0] = processorConstructor.newInstance(this.executor);                        } else {                            processorConstructor = processorType.getConstructor(Executor.class, SelectorProvider.class);                            pool[0] = processorConstructor.newInstance(this.executor,selectorProvider);                        }                    } catch (NoSuchMethodException e2) {                        // To the next step...                        try {                            processorConstructor = processorType.getConstructor();                            usesExecutorArg = false;                            pool[0] = processorConstructor.newInstance();                        } catch (NoSuchMethodException e3) {                            // To the next step...                        }                    }                }            } catch (RuntimeException re) {                LOGGER.error("Cannot create an IoProcessor :{}", re.getMessage());                throw re;            } catch (Exception e) {                String msg = "Failed to create a new instance of " + processorType.getName() + ":" + e.getMessage();                LOGGER.error(msg, e);                throw new RuntimeIoException(msg, e);            }            if (processorConstructor == null) {                // Raise an exception if no proper constructor is found.                String msg = String.valueOf(processorType) + " must have a public constructor with one "                        + ExecutorService.class.getSimpleName() + " parameter, a public constructor with one "                        + Executor.class.getSimpleName() + " parameter or a public default constructor.";                LOGGER.error(msg);                throw new IllegalArgumentException(msg);            }            // Constructor found now use it for all subsequent instantiations            for (int i = 1; i < pool.length; i++) {                try {                    if (usesExecutorArg) {                        if(selectorProvider==null) {                            pool[i] = processorConstructor.newInstance(this.executor);                        } else {                            pool[i] = processorConstructor.newInstance(this.executor, selectorProvider);                        }                    } else {                        pool[i] = processorConstructor.newInstance();                    }                } catch (Exception e) {                    // Won't happen because it has been done previously                }            }            success = true;        } finally {            if (!success) {                dispose();            }        }    }

创建完SimpleIoProcessorPool实例后在代码1处赋值给AbstractPollingIoAcceptor的processor属性

private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig, Executor executor, IoProcessor<S> processor,            boolean createdProcessor, SelectorProvider selectorProvider) {        super(sessionConfig, executor);        if (processor == null) {            throw new IllegalArgumentException("processor");        }        this.processor = processor;//1        this.createdProcessor = createdProcessor;        try {            // Initialize the selector            init(selectorProvider);            // The selector is now ready, we can switch the            // flag to true so that incoming connection can be accepted            selectable = true;        } catch (RuntimeException e) {            throw e;        } catch (Exception e) {            throw new RuntimeIoException("Failed to initialize.", e);        } finally {            if (!selectable) {                try {                    destroy();                } catch (Exception e) {                    ExceptionMonitor.getInstance().exceptionCaught(e);                }            }        }    }

创建完AbstractPollingIoAcceptor对象后会依次初始化父类对象AbstractIoAcceptor、AbstractIoService如图4所示


至此,NioSocketAcceptor对象就已经创建完毕了,创建完NioSocketAcceptor后一般要设置过滤器***Filter,处理器***Handler(这些在后面的文章中会介绍),绑定监听端口,本篇文章重点介绍mina在绑定端口时为我们做了哪些工作。

绑定端口的过程如图4所示,在第2.1.1.1步调用了startupAcceptor方法,具体代码如下,在代码1处创建了acceptor对象并在代码2处将acceptor交由线程池执行逻辑,这个线程池是在创建NioSocketAcceptor时创建的

private void startupAcceptor() throws InterruptedException {        // If the acceptor is not ready, clear the queues        // TODO : they should already be clean : do we have to do that ?        if (!selectable) {            registerQueue.clear();            cancelQueue.clear();        }        // start the acceptor if not already started        Acceptor acceptor = acceptorRef.get();        if (acceptor == null) {            lock.acquire();            acceptor = new Acceptor();  //①            if (acceptorRef.compareAndSet(null, acceptor)) {                executeWorker(acceptor); //②            } else {                lock.release();            }        }    }
Acceptor类的代码如下,while循环中的循环变量selectable在创建NioSocketAcceptor对象时被赋值selectable=true;所以while循环会一直进行,该类的功能就是判断是否有新的连接到来,如果有新的连接到来就调用processHandles方法将新的连接封装成IoSession,在代码1处将连接封装成了一个Session。

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) {//判断是否有新的连接到来                        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                    ExceptionMonitor.getInstance().exceptionCaught(cse);                    break;                } catch (Exception 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();                    }                }            }        }        /**         * This method will process new sessions for the Worker class.  All         * keys that have had their status updates as per the Selector.selectedKeys()         * method will be processed here.  Only keys that are ready to accept         * connections are handled here.         * <p/>         * Session objects are created by making new instances of SocketSessionImpl         * and passing the session object to the SocketIoProcessor class.         */        @SuppressWarnings("unchecked")        private void processHandles(Iterator<H> handles) throws Exception {            while (handles.hasNext()) {                H handle = handles.next();                handles.remove();                // Associates a new created connection to a processor,                // and get back a session                S session = accept(processor, handle);//①                if (session == null) {                    continue;                }                initSession(session, null, null);                // add the session to the SocketIoProcessor                session.getProcessor().add(session);//②            }        }    }
让我们看看是怎么将连接封装成session的,实际上就是创建了一个NioSocketSession对象,创建对象时将IoService对象、IoProcessor对象、SocketChannel对象传入作为NioSocketSession类的属性,从图3中可以看到,Acceptor线程将连接封装成IoSession后交给了IoProcessor线程来处理,还记得在创建NioSocketAcceptor对象时初始化了processor属性,并为该属性赋值了一个SimpleIoProcessorPool吗,在代码2处通过session.getProcessor()方法可以拿到SimpleIoProcessorPool对象的引用,让我们看看SimpleIoProcessorPool对象的add方法是怎么实现的

    @Override    protected NioSession accept(IoProcessor<NioSession> processor, ServerSocketChannel handle) throws Exception {        SelectionKey key = null;        if (handle != null) {            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);//①    }
add方法的实现如下:

    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;    }
又调用了一次getProcessor方法,这个getProcessor方法和之前的getProcessor方法完全不是一回事,之前的getProcessor方法是为了拿到SimpleIoProcessorPool对象,这个getProcessor方法是SimpleIoProcessorPool对象的一个方法,该方法的作用是根据sessionId在选出一个processor,然后调用add方法,在add方法的代码1处调用了startupProcessor方法,在该方法中,创建了一个Processor对象,并将该对象放到线程池中启动

    public final void add(S session) {        if (disposed || disposing) {            throw new IllegalStateException("Already disposed.");        }        // Adds the session to the newSession queue and starts the worker        newSessions.add(session);        startupProcessor();//①    }
    private void startupProcessor() {        Processor processor = processorRef.get();        if (processor == null) {            processor = new Processor();            if (processorRef.compareAndSet(null, processor)) {                executor.execute(new NamePreservingRunnable(processor, threadName));            }        }        // Just stop the select() and start it again, so that the processor        // can be activated immediately.        wakeup();    }
Processor主要负责具体的IO读写操作和执行后面的IoFilterChain和IoHandler逻辑

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 (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {                        // Last chance : the select() may have been                        // interrupted because we have had an closed channel.                        if (isBrokenConnection()) {                            LOG.warn("Broken connection");                        } 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();                        }                    }                    // Manage newly created session first                    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...                        process();//②                    }                    // Write the pending requests                    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()) {                        boolean hasKeys = false;                                                for (Iterator<S> i = allSessions(); i.hasNext();) {                            IoSession session = i.next();                                                        if (session.isActive()) {                                scheduleRemove((S)session);                                hasKeys = true;                            }                        }                        if (hasKeys) {                            wakeup();                        }                    }                } catch (ClosedSelectorException cse) {                    // If the selector has been closed, we can exit the loop                    // But first, dump a stack trace                    ExceptionMonitor.getInstance().exceptionCaught(cse);                    break;                } catch (Exception e) {                    ExceptionMonitor.getInstance().exceptionCaught(e);                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e1) {                        ExceptionMonitor.getInstance().exceptionCaught(e1);                    }                }            }            try {                synchronized (disposalLock) {                    if (disposing) {                        doDispose();                    }                }            } catch (Exception e) {                ExceptionMonitor.getInstance().exceptionCaught(e);            } finally {                disposalFuture.setValue(true);            }        }    }
在代码1处将客户端创建的新连接以事件的形式通过过滤器链传给IoHandler的SessionCreated和SessionOpend方法,如果不是新的连接而是消息到达,则会走到代码2处,该方法会将消息以事件的形式通过过滤器传递给IoHandler的MessageReceived方法。






1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小米手机前置摄像头打不开怎么办 mp4视频屏幕好小怎么办 投了简历没回应怎么办 借壳上市原股票怎么办 红米note3指纹解锁失灵怎么办 来分期刷脸失败怎么办 买到假货商家不承认怎么办 买到假货翻新货工商局怎么办 三星note8笔丢了怎么办 夜跑结束后该怎么办 lol客户端正在运行中怎么办 瑞和宝终端锁定怎么办 骊爽摩托车动力弱怎么办? 换了手机跟点微信怎么办 手机送点插头换了怎么办 校园网总是显示有代理软件怎么办 电脑连接无线网络需要登录怎么办 智校园密码忘了怎么办 ivvi手机开不开机怎么办 跑鞋子大了一码怎么办 洗浴搓澡出汗多该怎么办 联华超市卡丢了怎么办 飞利浦电视的应用打不开怎么办 贵阳市下岗职工办理门面补贴怎么办 陌陌上被骗了色怎么办 七位数的座机要怎么办 开通米粉卡店铺预留电话号码怎么办 办信用卡没有单位电话怎么办 qq账号永久封停怎么办 qq号给冻结了怎么办 微信被官方封2天怎么办 天虹的卡丢了怎么办 顺丰快递到不了的地区怎么办 信用卡兑换东西超过积分怎么办 新买的冰箱坏了怎么办 买新洗衣机出现克坏怎么办 京东当日达没到达怎么办 兴隆卡不能用了怎么办 龙津时代烂尾了怎么办 三星s8电耗尽无法开机怎么办 杜鹃买回来蔫了怎么办