`
scholers
  • 浏览: 620184 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

MINA2的多线程模型问题探讨

 
阅读更多
  最近和一友人沟通,他在一个项目中使用MINA2,他反馈在启动了服务端之后,发现IO阻塞和堆内存一直上升;





JCONSOLE面板中的解释:
阻塞总数

Blocked count is the total number of times that the thread blocked to enter or reenter a monitor. I.e. the number of times a thread has been in the java.lang.Thread.State.BLOCKED state.
当线程试图获取一个内部的对象锁(不是java.util.concurrent库中的锁),而锁被其它线程占有,则该线程进入阻塞状态。

等待总数
Waited count is the total number of times that the thread waited for notification. i.e. the number of times that a thread has been in the ava.lang.Thread.State.WAITING or java.lang.Thread.State.TIMED_WAITING state.
当线程等待另外一个线程通知调度器的一个条件的时候,它自己进入等待状态。在调用Object.wait()或Thread.join()方法,或者等待java.util.concurrent库中的Lock或Condition时,会出现等待状况。

后这两天我自己本地拿一个简单案例测试了下,
采用JAVA原生的newCachedThreadPool,以及MINA2自带的 OrderedThreadPoolExecutor 线程池,在性能上没有太大的区别;
本身他原来的代码中就是使用了一种线程池,将IO线程和工作线程(业务线程)区分开的,所以这个处理的堆内存升高和IO堵塞是正常现象,我本地代码实测,暂用的堆内存并不多,而且是快速回收了,并不影响后续业务;
下图中大量的thread就是由于使用了ExecutorFilter之后,MINA2服务器端产生的大量线程。


  在服务端启动的代码中有一段如下:
filterChainBuilder.addLast("threadPool", new ExecutorFilter());


仔细研究了下JCONSOLE中的阻塞总数,结合本地运行的经验,下图的结果:



  根据图里面的信息,发现在NioProcessor.java中
   @Override
    protected int select(long timeout) throws Exception {
        return selector.select(timeout);
    }

然后继续跟踪,在AbstractPollingIoProcessor.java类中
 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
                    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()) {
                        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);
            }
        }
    }

上述行发生了阻塞行为,这个类是干嘛的呢?这个类是MINA2中去轮训SELECTOR中是否有空闲的,查看一下selector的源码:
    /**
   [color=red][b]  * Selects a set of keys whose corresponding channels are ready for I/O
     * operations.
     *
     * <p> This method performs a blocking <a href="#selop">selection
     * operation</a>.  It returns only after at least one channel is selected,
     * this selector's {@link #wakeup wakeup} method is invoked, the current
     * thread is interrupted, or the given timeout period expires, whichever
     * comes first.
     *
     * <p> This method does not offer real-time guarantees: It schedules the
     * timeout as if by invoking the {@link Object#wait(long)} method. </p>[/b][/color]
     *
     * @param  timeout  If positive, block for up to <tt>timeout</tt>
     *                  milliseconds, more or less, while waiting for a
     *                  channel to become ready; if zero, block indefinitely;
     *                  must not be negative
     *
     * @return  The number of keys, possibly zero,
     *          whose ready-operation sets were updated
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @throws  ClosedSelectorException
     *          If this selector is closed
     *
     * @throws  IllegalArgumentException
     *          If the value of the timeout argument is negative
     */
    public abstract int select(long timeout)
	throws IOException;

仔细的看看注释:
Selects a set of keys whose corresponding channels are ready for I/O[color=red][/color]
原来是去轮训IO通道是否空闲,也就是说上述阻塞是发生在这里,也就是说IO通道被轮训发生的阻塞,所以说本文开头的阻塞数量是这样产生的:
当大量的并发消息过来,MINA2服务端需要轮训IO通道是否OK,OK之后就马上交给工作线程(如果有)去处理。所以从上面看来,这个是正常现象。在同时并发大量消息的时候,MINA2的IO通道还是会有堵塞。

另外:
  我们知道:MINA2采用的是责任链模式,在处理过程中插入不同的filter,来进行不同的处理。
当服务器端启动的时候,会启动默认的I0线程,也就是主线程,MINA2建议是CPU数量+1,如果代码中不增加上述一行,那么如果加入上述的ExecutorFilter之后,工作线程将会和IO线程分开,简单的例子,如果你要发一些消息给服务端,要服务端提交一个事务,如果不采用ExecutorFilter,那么IO线程就会一直等待这个事务结束,才会接着处理下一个任务,否则一直等待,如果采用了ExecutorFilter,那么IO线程会将任务交给ExecutorFilter的线程池,由线程池去处理事务,而IO线程就释放了,这样IO线程非常高效的能够同时处理很多的并发任务。
用new ExecutorFilter(),缺省使用OrderedThreadPoolExecutor线程池。需要注意的是这个线程池,对应同一个session而言,是顺序执行的.也就是说mina2里面的message处理都是顺序的,不会说出现在消息没有接受完毕之前关闭。
常见的也有这样的处理:修改成:
filterChainBuilder.addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool

()); 



  • 大小: 231.5 KB
  • 大小: 122.9 KB
  • 大小: 174.2 KB
  • 大小: 46 KB
1
0
分享到:
评论
5 楼 scholers 2014-07-03  
logicbaby 写道
qrong 写道
经测试,new ExecutorFilter(Executors.newCachedThreadPool())会导致在codec层收到的数据错乱,好大个坑。。。。。改回默认的orderThreadPool后无此问题。

很显然的问题,用CachedThreadPool不保证同一个session的消息有序被处理,改用UnorderedThreadPoolExecutor或者将codec filter在ExecutorFilter之前添加。


这位兄弟研究的很透彻!
4 楼 logicbaby 2014-06-04  
qrong 写道
经测试,new ExecutorFilter(Executors.newCachedThreadPool())会导致在codec层收到的数据错乱,好大个坑。。。。。改回默认的orderThreadPool后无此问题。

很显然的问题,用CachedThreadPool不保证同一个session的消息有序被处理,改用UnorderedThreadPoolExecutor或者将codec filter在ExecutorFilter之前添加。
3 楼 logicbaby 2014-06-04  
new ExecutorFilter(Executors.newCachedThreadPool())
用CachedThreadPool会创建过多的线程,如果有5000个活跃连接,这个东西就有可能创建5000个线程!无需顺序保证应该用new ExecutorFilter(new UnorderedThreadPoolExecutor());
2 楼 patrick_unicorn 2013-04-07  
qrong 写道
经测试,new ExecutorFilter(Executors.newCachedThreadPool())会导致在codec层收到的数据错乱,好大个坑。。。。。改回默认的orderThreadPool后无此问题。


这个问题应该是由于你将ExcecutorFilter放在了ProtocolCodecFilter之前造成的。
1 楼 qrong 2012-07-13  
经测试,new ExecutorFilter(Executors.newCachedThreadPool())会导致在codec层收到的数据错乱,好大个坑。。。。。改回默认的orderThreadPool后无此问题。

相关推荐

    mina2学习笔记

    - 揭示了Mina框架内部的工作原理,包括事件循环机制、线程模型等,帮助开发者更好地理解和使用Mina。 ##### 3.4 Mina的线程模型配置 - 解释了Mina中线程模型的配置方法,包括如何合理分配线程资源以达到最佳性能。...

    mina2推送demo客户端

    MINA2基于NIO(Non-blocking I/O)模型,允许在单个线程中处理多个连接,提高了系统并发能力。在"echo_client_udp"中,当客户端发送数据后,MINA2会异步等待服务器的响应,这样可以有效地避免线程阻塞,提高系统...

    mina2 源码 mina

    6. **Asynchronous I/O**:MINA基于NIO(Non-blocking I/O)模型,允许多个连接同时处理,而无需为每个连接创建新的线程,大大提升了服务器性能。 7. **Event-driven Model**:MINA采用事件驱动的设计,当网络事件...

    mina2 实例程序(socket通讯调用远程方法)

    为了提高性能,可以利用Mina2的多线程模型,实现并发处理多个连接。此外,还可以结合Spring框架,将Mina2集成到Spring Boot应用中,实现更灵活的服务注册和发现。 总结,Apache Mina2提供了一套强大的工具,使得...

    Java mina2源码

    源码分析还能帮助我们理解Java的多线程、并发控制和事件驱动编程等高级特性,提升我们的编程技能和解决问题的能力。 此外,对于Java开发者来说,熟悉Mina2源码有助于理解其他类似的网络通信框架,比如Netty,因为...

    mina

    4. **Apache MINA 线程模型配置 .shtml**:这部分内容可能专注于MINA的线程模型,MINA支持多种线程模型,如NIO多路复用器、简单的线程池等,以优化性能和并发处理能力。 5. **NIO_TEST**:这可能是一些与Java NIO...

    MINA1.7包(源码)

    4. **多线程支持**:MINA支持多线程模型,可以灵活配置工作线程数量,以适应不同场景的需求。 5. **缓冲区管理**:MINA提供了高效的数据缓冲区管理机制,能够减少数据拷贝,提高数据处理速度。 6. **编码解码器**...

    springboot 深度整合mina开箱即用

    同时,Mina提供了异步编程模型,可以通过多线程和事件循环来提高处理能力。 总结来说,Spring Boot深度整合Mina可以帮助我们快速构建高性能的网络服务,降低了开发复杂性。这个开箱即用的方案对于新手开发者来说,...

    mina 上传文件和文件名

    MINA的核心特性包括非阻塞I/O、事件驱动的架构、多线程模型和丰富的缓冲区管理。非阻塞I/O使得MINA能够处理大量并发连接,提高了系统的整体效率。事件驱动架构允许程序响应网络事件,如连接建立、数据读取或写入等,...

    mina2.0 英文版 中文版 官方文档

    7. **线程模型**:MINA 允许开发者自定义线程模型,如单线程、多线程或无守护线程模型,以适应不同场景的需求。 8. **配置与优化**:MINA 提供了丰富的配置选项,如缓冲区大小、心跳间隔、超时设置等,用于优化网络...

    mina的高级使用,mina文件图片传送,mina发送文件,mina报文处理,mina发送xml和json

    通过使用I/O多路复用技术,如NIO(非阻塞I/O),Mina能够高效地管理多个连接,而无需为每个连接创建单独的线程。这极大地降低了系统资源的消耗,提高了服务器的并发能力。 2. **Mina文件图片传送** 在Mina中,我们...

    JAVA mina 框架源码

    5. **线程模型**:Mina的线程模型允许用户自定义,如单线程、多线程或者无线程模型,以适应不同的应用场景。 6. **异常处理**:Mina对网络异常进行了封装,通过IoException和IoFuture来处理网络通信中的错误和异常...

    MINA2 教程 socket

    在这个"MINA2 教程 socket"中,我们将探讨如何使用MINA2来实现服务器与客户端之间的通信,并且支持同时在线连接,以及如何利用`newFixedThreadPool`线程池来优化处理并发连接。 1. **MINA2基本概念**: MINA2的...

    MINA客户端与服务端通信实例

    此外,MINA支持多线程模型,可以根据需求选择合适的模型以优化性能。 对于压缩包中的"jar包下载",这可能是指提供了MINA库的JAR文件,开发者可以直接引入到项目中使用,无需自行编译源码。在使用过程中,确保JAR...

    Mina 框架研究与实现

    它的功能涵盖数据流到消息对象的转换、日志记录、多线程I/O工作等。这意味着IoFilter不仅负责数据格式的转换,还能进行多方面的任务协调和管理,极大地增强了Mina框架的功能性和实用性。 #### 消息处理模型 在Mina...

    mina即时聊天demo

    需要注意的是,由于Android平台的限制,需要适配多线程和网络访问策略。 6. **Demo学习要点** - 分析服务端和客户端的启动与监听过程,理解如何创建IoAcceptor和IoConnector。 - 研究数据的编码和解码过程,熟悉...

    给予mina 协议进行大数据传输

    1. **非阻塞I/O**:MINA使用Java NIO(非阻塞I/O)库,这允许在单个线程中处理多个连接,极大地提高了系统资源的利用率和并发性能。 2. **事件驱动**:MINA基于事件驱动的设计,通过监听网络事件(如连接建立、数据...

    mina2.0.3源代码

    MINA的异步模型有助于避免线程池中的线程被长时间占用,优化了资源利用率。 3. **传输层抽象**: MINA对TCP和UDP进行了抽象,开发者可以使用相同的API来处理这两种不同类型的传输。这样,你可以轻松地在TCP的可靠...

    MINA文档

    例如,单线程模型适合简单应用,而多线程模型可以处理高并发情况。 从压缩包文件的名称可以看出,其中包含的内容涵盖了MINA的快速开发、参考手册、使用手记和线程模型配置等多个方面: - **基于MINA框架快速开发...

    深入理解Apache Mina

    本资料集合对Apache Mina进行了深入探讨,涵盖了Mina的关键组件和概念,包括IoFilter、IoHandler、ByteBuffer以及线程模型等。 1. **IoFilter与IoHandler**: IoFilter是Mina中的过滤器机制,它允许在数据传输过程...

Global site tag (gtag.js) - Google Analytics