AIO与BIO接口性能对比

来源:互联网 发布:小说免费下载软件 编辑:程序博客网 时间:2024/05/17 03:01
Servlet 3.0标准新增了诸多特性,异步处理支持是令开发者最为关注的一个特性。本文就将详细对比传统的Servlet与异步Servlet在开发以及性能上的差别,分析异步Servlet为何会提升Java Web应用的性能。在进行性能分析前,先简单介绍什么是同步异步、什么是阻塞非阻塞,以及AIO、NIO、BIO的概念。

基础知识

1 什么是同步与异步、阻塞与非阻塞

同步:自己去银行取钱(JAVA自己处理IO读写)
异步:委托别人拿银行卡去银行取钱,然后给你(Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API)
阻塞:ATM排队取款,只能傻等(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回)
非阻塞:柜台取款,取个号,然后坐在椅子上做其它事,广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)。

2 什么是BIO、NIO、AIO

2.1 BIO 
    同步并阻塞,服务器实现模式为一个连接一个线程,每个线程亲自处理io并且一直等待io的完成,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。当然可以通过线程池机制改善,但当线程的数量增加过多,标准 I/O 的不足就表现出来了。由于要在线程间进行上下文切换,因此 CPU 简直变成了超载。
BIO的局限:对每一个客户端的socket连接,IO都需要一个线程来处理,而且在此期间,这个线程一直被占用,直到socket关闭。在这期间,tcp的连接、数据的读取、数据的返回都是被阻塞的,大量的浪费了cpu的时间片和线程占用的内存资源。 

2.2 NIO (new IO) 从jdk1.4开始 
    同步非阻塞。客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到有I/O请求时才启动一个线程进行处理。
NIO用一个线程来轮询监控多个数据传输通道,哪个通道准备好了(即有了一组可以处理的数据),就处理哪个通道。 
例如:服务器端保存一个Socket连接列表,然后对这个列表进行轮询,如果发现某个Socket端口上有数据可读时,则调用该socket连接的读操作;如果发现某个 Socket端口上有数据可写时,则调用该socket连接的写操作;如果某个端口的Socket连接已经中断,则调用相应的方法关闭该端口。
这样能充分利用服务器资源,效率得到了很大提高。 但是仍存在的一个问题就是需要轮询线程进行不断的轮询查看数据是否准备就绪,因为也会浪费一些系统资源。

2.3 AIO (Asynchronous io、NIO.2)  从jdk1.7开始 
    异步非阻塞,服务器实现模式为一个有效请求一个线程。每个线程不必处理io,而是交给服务器处理,并且每个线程也不必等待处理的完成,而是当os处理完了以后通知服务器,这时候服务器再启动线程去处理。AIO模式下就不存在轮询带来的系统资源的浪费了,因此效率更高一些。

3 结论
    在连接数不多的情况下,传统IO模式编写方便,响应快速,但是随着连接的不断增多,传统的IO模式已不能满足需求,因此出现了非阻塞IO。非阻塞IO操作中处理10000个连接不再需要10000个线程,你可以用1000个线程,甚至100个线程来处理。当发送请求到服务器,服务器把这个请求当作一个请求"事件",并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还,因此一个线程就可以异步的处理多个事件。
   
测试分析


1 测试结果:

2 测试环境:

2.1 AIO:

      jetty9.3.1

      jdk8

      servlet3.1

2.2 BIO:

      jetty7.6.5

      jdk6

      servlet2.5

2.3 本机环境:
    8G、4核、台式机

2.4 测试方法介绍:
    http://www.atatech.org/articles/57011

    我通过简单的sleep操作模拟IO操作所需要的时间。针对BIO的测试,为了模拟负载,我通过JMeter创建了一个简单的测试。针对不同的sleep时间,设置不同的每秒并发线程数,每个线程不停的运行,每次都会请求这个BIO服务,执行五分钟-十分钟等数据稳定以后记录数据。在测试AIO的时候,除了设置的并发数要大一些,其它过程和BIO测试的过程一样,详情可参考上面的测试方法。由上图可以看出jetty7.6.5配置的默认工作线程数在250左右。当IO操作所需时间较小时,性能还能满足大多数的要求,但当IO操作所需时间较长时,如上图中sleep 10s的时候,QPS只有25,这是远不能满足要求的。通过上图还可以看出,AIO的QPS要远高于BIO,随着IO操作所需要的时间的增长,AIO模式的优越性比BIO模式显得愈发明显。当操作时间10s的时候,AIO操作的QPS是BIO的40倍。
:本测试中测试环境和被测试服务在同一台机器上进行,在进行AIO测试的时候,Jmeter并发量过大和本机维护的线程池过大导致本机CPU资源几乎耗尽,从而影响了AIO接口的性能,实际上的AIO与BIO的性能差距要大于本测试结果。

AIO模拟实现

基于spring boot框架。
1.自定义线程池:

2.异步请求service:

3.spring boot开启异步支持:@EnableAsync


结论:
   
servlet异步模式比起同步的优势,借助官方的一句话就是:
    The benefit is not directly about "performance gain". The purpose of those methods is to avoid a request thread (in async mode) from blocking when it reads input (POST) data or writes the document.
      BIO模式已不能满足如今高并发的需求,条件具备的情况下,建议升级AIO模式,性能一定爽翻天。

0 0