tomcat一共有四种线程模型,如下:
名称 | 描述 |
---|---|
BIO | 阻塞式IO,采用传统的java IO进行操作,该模式下每个请求都会创建一个线程,适用于并发量小的环境 |
NIO | 同步非阻塞,tomcat8.0后的默认模式 |
APR | tomcat以JNI形式调用http服务器的核心动态链接库来处理文件读取和网络传输操作,需要编译安装APR库 |
AIO | 异步非阻塞,tomcat8.0后支持 |
默认的配置文件:
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Service>
只要将protocol替换为以下几种即可:
BIO: protocol =" org.apache.coyote.http11.Http11Protocol" (tomcat9不支持)
NIO: protocol =“org.apache.coyote.http11.Http11NioProtocol”
AIO: protocol =“org.apache.coyote.http11.Http11Nio2Protocol”
APR: protocol =“org.apache.coyote.http11.Http11AprProtocol”
同步:自己亲自去买饭。(Java自己处理IO读写)
异步:点外卖,自己用走路去饭馆的时间去干别的事(Java将IO读写委托给操作系统处理,需要将数据缓冲区地址和大小传给操作系统(送餐地址,数量)
阻塞:食堂排队,只能等待(使用阻塞IO是,Java调用会一直阻塞到读写完成才返回)。
非阻塞:取号,坐在位置上休息,等阿姨喊,同时可以询问是否轮到自己了(使用非阻塞IO时,如果不能读写,Java调用会马上返回,当IO事件分发器会通知可读写时再继续读写 ,不断循环到读写完成)。
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
启动时,JIoEndpoint组件内部的Acceptor组件将启动某个端口的监听,一个请求到来后将被扔进线程池Executor,线程池进行任务处理处理,处理过程中将通过Http11Processor组件对HTTP协议解析并传递到Engine容器继续处理。
Tomcat的流量控制是通过AQS(AbstractQueuedSynchronizer)并发框架实现的,通过AQS实现更有灵活性和定制型。
LimitLatch用来控制tomcat的流量,每接收一个套接字(Socket)那么count+1,反之则减少 。如果超过最大限制,AQS将接收线程阻塞,停止接收套接字,直到某些套接字处理完关闭后重新唤起接收线程往下接收套接字。
Tomcat默认同时接收的客户端连接数为200,但可以通过server.xml中的
节点的maxConnections
进行调节,BIO模式下,最大连接数与最大线程数比例为1:1。
同时,与最大连接数相关的还有acceptCount
参数,默认值为100。可以参考一下情形:
1)接受一个请求,此时tomcat起动的线程数没有到达maxThreads,tomcat会起动一个线程来处理此请求。
2)接受一个请求,此时tomcat起动的线程数已经到达maxThreads,tomcat会把此请求放入等待队列,等待空闲线程。
3)接受一个请求,此时tomcat起动的线程数已经到达maxThreads,等待队列中的请求个数也达到了acceptCount,此时tomcat会直接拒绝此次请求,返回connection refused。
与BIO相比,NIO多了Poller组件,它可以在非阻塞I/O方式下轮询多个客户端连接,不断检测,处理各种事件,例如不断检测各个连接是否可读,对于可读的客户端连接尝试进行读取并解析消息报文。
Poller池的大小应为:Math.min(2,Runtime.getRuntime().availableProcessors())
打开Jmeter官网,进行工具的下载,http://jmeter.apache.org/download_jmeter.cgi
解压到相应的目录后,以管理员的身份打开bin目录下的jmeter.bat。
启动后等待一段时间会弹出图形化界面:
启动一个java web程序,开放端口localhost:8080,使用Jmeter对其进行压测,步骤如下:
下图第一个红框内的协议、IP、端口不需要设置,会使用步骤3中设置的默认值,只需设置请求路径Path
即可,这里填入/task2_1_war_exploded/hello
,这是我的接口路径
1)Label:每个 JMeter 的 element(例如 HTTP Request)都有一个 Name 属性,label显示的就是 Name 属性的值 。
2)#Samples:表示你这次测试中一共发出了多少个请求。
3)Average:平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间。(单位ms)
4)Median:中位数,也就是 50% 用户的响应时间。
5)90% Line:90% 用户的响应时间。
6)Min:最小响应时间。
7)Max:最大响应时间。
8)Error%:本次测试中出现错误的请求的数量/请求的总数。
9)Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second),当使用了 Transaction Controller 时,也可以表示类似 LoadRunner 的 Transaction per Second 数。
10)KB/Sec:每秒从服务器端接收到的数据量,相当于LoadRunner中的Throughput/Sec 。
tomcat模式 | 并发数 | 样本数 | 平均响应时间 | 吞吐量 | 错误率 | 接收Kb/s | 发送Kb/s |
---|---|---|---|---|---|---|---|
nio | 200 | 20000 | 147 | 1176.3/s | 18.52% | 817.18 | 134.78 |
nio | 300 | 30000 | 236 | 1029.1/s | 45.02% | 1278.89 | 79.57 |
nio | 500 | 50000 | 493 | 842.3/s | 70.38% | 1490.38 | 35.09 |
aio | 200 | 20000 | 114 | 1357.1/s | 18.70% | 947.83 | 155.15 |
aio | 300 | 30000 | 254 | 949.5/s | 59.03% | 1455.15 | 54.71 |
aio | 500 | 50000 | 401 | 1020.9/s | 64.70% | 1687.59 | 50.68 |
可以发现两者性能差不多。同时可以发现,在当前配置下,并发数在200时(循环次数为100)就有点撑不住了。