前面两节,我们分别看了BIO和NIO的两种模式Tomcat的实现方式。

BIO的方式,就是传统的一线程,一请求的模式,也就是说,当同时又1000个请求过来,如果Tomcat设置了最大Accept线程数为500,那么第一批的500个线程直接进入线程池中进行执行,而其余500个根据Accept的限制的数量在服务器端的操作系统的内核位置的socket缓冲区进行阻塞,一直到前面500个线程处理完了之后,Acceptor组件再逐步的放进来。

分析一下,这种模式的BIO的好处,可以让一个请求在cpu轮转时间片切换中最大限度的执行,如果业务请求不是很长时间的事务处理,通常在一个时间片内肯定能做完当前的请求,这样的效率算是相当的高了,因为其减少了最耗时也是最头疼的线程上下文切换;
1.但是,如果事务执行比较长的时间,例如等待一个IO数据库的操作,那么这个工作线程就会根据cpu轮转不断的进行切换,因为请求数在大并发中很多,所以不得不设置一个很高的Accept线程数,那么从cpu的耗费的资源上来看,甚至有70%的时间浪费在线程切换中,而没有真正的时间去做请求处理和业务,这是第一个问题。
2.其次,BIO每一次链接的建立和释放都需要重新来过一遍,例如一个socket进来之后,通常会对其SocketOptions的属性进行设置,包括各种Connector中配置都要与其进行一一对应,加上前面说的socket的建立,很多请求通道的资源的初始化都得重新创建,得不到复用,这个是第二个问题。
3.最后,BIO方式网络IO的阻塞等待是会让Accept线程工作效率降低很多的。
所以,基于这3个问题,特别是最后一个问题,引出了NIO的模型。

NIO的架构分为三个线程池,这里再次梳理一下:
1.Acceptor专门接socket请求,当发现又请求进来后,基于Tomcat配置的SocketOptions和一些属性的设置完毕,包装成SocketChannel,也就是NIO的socket通道抽象,塞入PollerEvent直接扔到队列当中;
2.Poller线程从队列中挨个获取PollerEvent,调用Poller线程自己持有的selector选择器,注册SocketChannel到当前的selector选择器中,然后进行selectKey的工作,这样Acceptor传递过来的SocketChannel中感兴趣的事件,就会被轮询出来,当接收事件接收之后,需要注册OP_READ事件或者OP_WRITE事件,当OP_READ事件或者OP_WRITE事件发生时,开始调用工作线程池;
3.工作线程池就是SocketProcessor,这个就是具体的工作线程,SocketProcessor的任务就是Poller线程从SocketChannel通道中轮询出来的数据包,进行解析,传递给后端的handler进行http的解析,解析出来的Request,Reponse对象,,直接调用CoyoteAdapter传递到后端的容器,通过Mapper,映射到对应的业务Servlet中。可以看到,从SocketProcessor一直到最终的业务Servlet实现,这些都是一个线程,这个线程就是工作线程。

对比Tomcat的BIO的架构,因为没有selector轮询的操作,所以并没有Poller线程,BIO中的Acceptor线程的作用依然是对socket简单的处理和属性包装,然后将socket直接扔到工作线程中来。NIO相当于是多了一个线程池,从流程上来讲,应该是多了一道手续,但是通过NIO本身基于事件触发的机制造成,Acceptor线程没必要设置的过多,这样从线程的数量上来看,大大的减少线程切换的频率,其次基于事件进行触发,将Acceptor线程执行效率中的网络IO延迟降低到最低,大大提升了Acceptor线程的执行效率。从这两点上来看,Tomcat的NIO在前面分析的BIO的三个问题中第一个问题,和第三个问题都有所改善,特别是第三个问题,全面进行了升级。

但是,对于BIO中的第一个问题,由后端事务时间过长导致工作线程池一直在运行,并且运行在一个高峰的数值,不断的进行切换,这种问题,NIO通道也没办法进行处理,这个是由业务来决定的,NIO只能保证降低的是Acceptor线程线程数,对业务帮助也是无能为力的,如果要提升这部分的效率,那就需要应用进行修改,优化JDBC和数据库,或者将业务切段来做,让事务时间尽量控制在一个可控的范畴之内。

对于第二个问题,无论是单纯的NIO和BIO通道都没有办法进行解决,但是HTTP协议中对链接的复用进行更新,在HTTP1.1中,这个keepalive是加到http请求头中的:
Keep-Alive: timeout=5, max=100 
timeout:过期时间5秒(对应httpd.conf里的参数是:KeepAliveTimeout);
max是最多能承受一百次请求的共享复用,就是在timeout时间内又有新的连接过来,同时max会自动减1,直到为0,强制断掉。 

对应的Tomcat的服务器端的配置:

keepAliveTimeout:表示在下次请求过来之前,tomcat保持该连接多久。这就是说假如客户端不断有请求过来,且为超过过期时间,则该连接将一直保持。
maxKeepAliveRequests:表示该连接最大支持的请求数。超过该请求数的连接也将被关闭(此时就会返回一个Connection: close头给客户端)。

如果配置了上述的内容,可以解决BIO上面提出的第二个问题,当一个页面中的第一个请求后,后面的连接可以复用这个socket或者是socketchannel,不用再accept三次握手或者SSL握手了,相当于高效的推动了整体Tomcat的时间链条的处理效率,而对于keepAlive属性的加入,通过BIO和NIO对比测试发现,相当于放大了NIO的优势,导致NIO的测试结果要明显高于BIO一个水平线上,这也就是目前http1.1协议中,为什么Tomcat后续版本默认就是NIO的原因;而如果没有keepAlive属性加入,在大多数的场景下,NIO并没有拉开与BIO太大的差距,甚至有一些场景上,Tomcat的BIO模式反倒是比NIO要高;

这里单纯的对比性能没有任何的意义,因为性能测试是测试在不同应用类型,不同硬件环境,不同软甲版本,甚至是不同jdk性能差异都很大,客观因素很多,而且Tomcat的web服务器目前在企业应用或者是互联网应用上来看,都是其链条中的微小的时间占比环节,甚至有的长事务处理链条中,Tomcat这块占比不到1%,当然对于学习和研究,更高更快更强是技术追求的目的,这个就另当别论了。

后续会详细分析一下keepAlive的实现,还有APR,NIO2两个通道的实现,敬请期待。

c.BIO连接器与NIO连接器的对比的更多相关文章

  1. d.BIO连接器与NIO连接器的对比之二

    前面在Tomcat中讲解了两个通道,BIO和NIO,我们这里来通过两端程序,简单模拟两个通道,找找异同点: BIO: 1. public class SocketServer {    public ...

  2. Java--Stream,NIO ByteBuffer,NIO MappedByteBuffer性能对比

    目前Java中最IO有多种文件读取的方法,本文章对比Stream,NIO ByteBuffer,NIO MappedByteBuffer的性能,让我们知道到底怎么能写出性能高的文件读取代码. pack ...

  3. 连接器|网络滤波连接器|电脑连接器|RJ45变压器-华联威电子有限公司

    连接器|网络滤波连接器|电脑连接器|RJ45变压器-华联威电子有限公司  

  4. Revit MEP API找到连接器连接的连接器

    通过conn.AllRefs;可以找到与之连接的连接器. //连接器连接的连接器 [TransactionAttribute(Autodesk.Revit.Attributes.Transaction ...

  5. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  6. EMS设置发送连接器和接收连接器邮件大小

    任务:通过EMS命令设置发送接收连接器和接收连接器的邮件大小限制值为50MB. 以Exchange管理员身份打开EMS控制台.在PowerShell命令提示符下. 键入以下命令设置接收-连接器的最大邮 ...

  7. 1、nio说明 和 对比bio

    nio和bio的区别 bio: 面向流的. 单向的. 阻塞的,这也是b这个的由来. nio: 面向块的.(buffer) 双向的. 非阻塞的.同步的编程方式.是一种select模型 nio编程的常规步 ...

  8. Java NIO学习系列四:NIO和IO对比

    前面的一些文章中我总结了一些Java IO和NIO相关的主要知识点,也是管中窥豹,IO类库已经功能很强大了,但是Java 为什么又要引入NIO,这是我一直不是很清楚的?前面也只是简单提及了一下:因为性 ...

  9. Java NIO学习笔记九 NIO与IO对比

    Java NIO与IO Java nio 和io 到底有什么区别,以及什么时候使用nio和io,本文做一个比较. Java NIO和IO之间的主要区别 下表总结了Java NIO和IO之间的主要区别, ...

随机推荐

  1. Python中的函数修饰符@

    首先,什么是函数修饰符?函数修饰符就是对原有函数做一层包装.比如有以下两个函数: 复制代码 def func1(): print 'I am function func1' def func2(): ...

  2. ios开发xcode中设置代码块

    在开发中有很多重复的代码,很多开发者把常用的代码做成代码块提高开发效率. 在xcode里选中代码块的时候总是很不容易,点击选中的代码(文字),不要移动和松开鼠标左键,当竖线变成像拉长了的x(我也不知道 ...

  3. iOS:一些常用的框架

    一.更多框架请看链接: https://github.com/xiayuanquan/TimLiu-iOS http://www.cnblogs.com/XYQ-208910/p/5901012.ht ...

  4. AngularJS Best Practices: SEO

    Google can execute AJAX & JavaScript for indexing, you can read the below link for more detailed ...

  5. RFC-2068-http

    本文档规定了互联网社区的标准组协议,并需要讨论和建议以便更加完善.请参考 “互联网官方协议标准”(STD 1)来了解本协议的标准化状态.本协议不限流传发布. 版权声明 Copyright (C) Th ...

  6. Bootstrap modal被sliverlight掩盖。

    Bootstrap中的modal被silverlight遮挡了,解决方案: <object id='xx'....> ... <param name="windowless ...

  7. 模板 KMP

    [模板]KMP int next[N]; char str1[M],str2[N]; //str1 长,str2 短 //len1,len2,对应str1,str2的长 void get_next(i ...

  8. Java关于md5加密

    package com.mi.util; /** * md5+salt 长度为32的加密 * @author admin * */ public class MD5 { public static v ...

  9. 如何知道btree树的高度

    来自网络,总结到这里: 当我想看btree树高度的时候,筛选出来这篇文章"为什么 B-tree 在不同著作中度的定义有一定差别?",知道了高度的算法是这个公式:但是里面又提高t和出 ...

  10. (转) CCTextFieldTTF输入框

    CCTextFieldTTF输入框 分类: cocos2d-x 2013-04-08 16:32 2964人阅读 评论(1) 收藏 举报 新建工程,testInput 修改HelloWorldScen ...