Web服务异步化:web
包括两部分,数据传输层异步化(你们已经熟知的NIO),Http业务请求异步化(continuations,servlet3.0)。服务异步处理我将会有一个详细的说明文档(服务异步化的概念,服务异步化的几种标准实现,服务异步化容器的特色),后续给出。后端
Web服务异步化测试缘由:缓存
TOP应用特殊性:tomcat
1.自身服务能力由后端的服务能力决定。(对同步Web请求的转发)安全
2.后端服务部署等同性,但要求服务互不影响。服务器
第一点致使TOP没法预估自身服务能力(不一样后端服务处理速度下的TOP有不同的支持能力),同时也没法应对在后端服务异常的状况下,总体的服务质量。并发
第二点致使TOP只有在物理上分隔不一样服务提供者所对应的TOP集群(资源浪费,同时没法动态调整资源来知足服务变化状况)。eclipse
所以须要对TOP实施web服务异步处理的测试。这里简单的说一下服务异步化的使用场景须要知足的几个特色:异步
1. 处理耗时大多消耗在于对后端或者外部服务资源的请求上。jvm
2. 后端或者外部资源在更多的流量下不会成为瓶颈。
拿TOP来解释一下:TOP自身处理主要包括路由,安全,流控等,可是最耗时的是在请求后端各个淘宝团队的服务。其次当先后端服务能力还没有达到真实的处理高峰,所以不少请求被堵在TOP平台,特别是当某些服务异常的时候,另外一些服务就会被拖累没法获得充分利用。(固然咱们有流控,发现后端服务能力已经成为瓶颈的时候能够对单独服务做限制)。
长话短说,上测试结果……
环境说明:
Linux 2.6.9-55.ELsmp
4 Core
4 G Memory
JDK 1.6.0_07
测试工具:loadRunner 9.5
测试涉及到了四种容器部署:后面都会用缩写在测试结果上注明
1. Apache + modjk + Jboss(后面缩写为Jboss):
此模式Apache配置以下:
<IfModule mpm_worker_module>
ServerLimit 80
ThreadLimit 128
StartServers 10
MaxClients 10240
MinSpareThreads 64
MaxSpareThreads 800
ThreadsPerChild 128
MaxRequestsPerChild 10000
</IfModule>
Jboss的web容器配置以下:
<Connector port="8009" address="${jboss.bind.address}" connectionTimeout="8000" protocol="AJP/1.3" maxThreads="500" minSpareThreads="40" maxSpareThreads="75" maxPostSize="512000" acceptCount="300" bufferSize="16384" emptySessionPath="false" enableLookups="false" redirectPort="8443" URIEncoding="utf-8"/>
Jboss的web部分以APR模式启动。
2. Tomcat6(APR)
关键配置以下:
<Executor name="topThreadPool" namePrefix="top-exec-"
maxThreads="500" minSpareThreads="40"/>
<Connector port="7777" protocol="HTTP/1.1"
executor="topThreadPool" connectionTimeout="20000" acceptCount="1000"
redirectPort="8444" />
3. Tomcat7 RC 4(APR)
关键配置以下:
<Executor name="topThreadPool" namePrefix="top-exec-"
maxThreads="500" minSpareThreads="4"/>
<Connector executor="topThreadPool" port="3333" protocol="HTTP/1.1"
connectionTimeout="20000" acceptCount="1000"
redirectPort="6443" />
4. Jetty7
关键配置以下:
<Set name="ThreadPool">
<!-- Default queued blocking threadpool -->
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">500</Set>
</Set>
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<Set name="host"><SystemProperty name="jetty.host" /></Set>
<Set name="port"><SystemProperty name="jetty.port" default="6060"/></Set>
<Set name="maxIdleTime">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">20000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New>
</Arg>
</Call>
附注:
Asyn表示异步模式,syn表示同步。Asyn中还分红resume和complete两种方式,后续在介绍技术背景的时候详细描述。
对于服务端的load不是每个测试都作了记录,选取了最全面的1500并发用户作了测试。
最大服务请求处理数是经过应用自身实现,具体代码能够参考后面的代码附件。
测试结果以下:
场景1:500 并发用户场景下,后端服务一次请求消耗3秒钟
容器 |
模式 |
TPS |
Average Response Time(s) |
Average Throughput(byte/s) |
最大请求处理数 |
Success rate |
Jetty7 |
asyn(resume) |
162.8 |
3.008 |
38430 |
500 |
100% |
Tomcat6 |
syn |
163.3 |
3.005 |
18453 |
500 |
100% |
这个场景测试的目的是比较在线程池资源足够的时候,异步和同步的差异。(也就是TOP服务器全部的资源处于正常服务,前台请求没有由于前段链接被消耗完,致使服务质量下降)
能够看到,在TPS和Response Time上二者基本上没有太大差异,TPS就等于500/3=167左右(3秒一个请求,所以用这种简单算式能够算出),响应时间也较为正常。当时我发如今每秒吞吐量上有些差异,后来单个测试case跑了一下,发现是返回的http header比较大,应该是在作异步化时重入等做的一些标识(后面其余容器的异步化也是同样)。
最大处理请求数都在服务端后台看到是500,等同于最大的并发用户数。
场景2:1000 并发用户场景下,后端服务一次请求消耗3秒钟
容器 |
模式 |
TPS |
Average Response Time(s) |
Average Throughput(byte/s) |
最大请求处理数 |
Success rate |
Jetty7 |
asyn(resume) |
317.06 |
3.036 |
74826 |
1000 |
100% |
Tomcat6 |
syn |
163.323 |
5.904 |
18455 |
500 |
100% |
场景2就在资源不足的状况下,比较异步服务请求与同步请求处理能力。(例如因为后端某些服务比较慢,致使前段的服务器可以承载的请求数目超过了线程数)
这个场景的结果能够看到TPS在异步模式下与并发用户数呈现同步增加,就比如配置了1000个线程做为线程池同样,一样在后端打出的最大请求数上也证实了这一点,所以前段线程池的服务能力在异步的状况下充分复用(固然这里使用的异步服务处理使用的是NIO而不是BIO的Connector)。一样在吞吐量上依然是增长的,因为异步附加的内容。
场景3:1500 并发用户场景下,后端服务一次请求消耗3秒钟
容器 |
模式 |
TPS |
Average Response Time(s) |
Average Throughput(byte/s) |
Server Load |
Success rate |
Jboss |
syn |
75.546 |
5.347 |
21002 |
0.115 |
68% |
Jetty7 |
Syn |
163.156 |
8.788 |
19252 |
0.129 |
100% |
Jetty7 |
Asyn(complete) |
432.153 |
3.312 |
76491 |
2.649 |
100% |
Jetty7 |
Asyn(resume) |
423.638 |
3.375 |
99979 |
2.826 |
100% |
Tomcat6 |
Syn |
163.836 |
8.75 |
18513 |
0.258 |
100% |
Tomcat7 |
ASyn |
423.501 |
3.328 |
54632 |
1.064 |
99.3% |
场景三比对了现有TOP的部署模式(Apache + modjk + Jboss)和Jetty7的同步模式,两种异步模式,Tomcat同步模式,Tomcat的servlet3.0异步模式的处理状况。根据测试能够获得的信息以下:
1. 现有部署方式在后端服务处理耗时较大的状况下,处理能力不如Jetty7和Tomcat6,同时出错率很高。
2. Jetty7的同步处理测试结果和Tomcat6的同步处理测试结果很相似,可是load方面jetty7更好。
3. 异步处理方面Jetty7的两种方式基本上差异不大(后续还须要深刻源码看看对于数据缓存资源复用的情况),Tomcat7的异步处理成功率有些问题(错误多半是在获取response回写的时候,response已经被提早释放,看来Tomcat7仍是须要一些时间来考验),load上来讲tomcat结果比较好。
4. 异步请求在提升处理能力的状况下,对于资源消耗也较大(线程切换较为频繁),不过仍是在承受范围。
三个场景组合比较:
容器 |
并发用户 |
模式 |
TPS |
Average Response Time(s) |
Average Throughput(byte/s) |
Success rate |
Jetty7 |
500 |
asyn(resume) |
162.8 |
3.008 |
38430 |
100% |
Jetty7 |
1000 |
asyn(resume) |
317.06 |
3.036 |
74826 |
100% |
Jetty7 |
1500 |
asyn(resume) |
423.638 |
3.375 |
99979 |
100% |
Tomcat6 |
500 |
syn |
163.3 |
3.005 |
18453 |
100% |
Tomcat6 |
1000 |
syn |
163.323 |
5.904 |
18455 |
100% |
Tomcat6 |
1500 |
Syn |
163.836 |
8.75 |
18513 |
100% |
最后将三个场景合并起来作一个简单的比较,获得信息以下:
1. 随着并发用户的增长,自己异步处理也会有衰减,同时对于性能消耗(线程切换)也会不断增加。
2. 异步化在消息头上会增长一些数据,会增长回写的带宽消耗(不过量不大),一个请求增长了100byte左右的消息数据。
测试总结:
1. Web请求异步化在TOP很合适。
重复两个条件:
a. 处理耗时大多消耗在于对后端或者外部服务资源的请求上。
b. 后端或者外部资源在更多的流量下不会成为瓶颈。
2. Web请求异步化在Jetty6到7上已经经历了2年多的成长(Google App Engine , Yahoo Hadoop),稳定性和效率较好。(同时不少优化能够经过扩展自行定制,jetty的可扩展性很好)Tomcat7在servlet3上处于刚发布阶段,还有待继续完善。(Servlet3的另外一种模式还没有执行成功,直接致使jvm退出)
后续须要继续跟进的:
1. 对于大数据量请求的容器间性能对比。(图片上传或者大数据量的Post请求)
2. 容器安全性。(是否容易被***)
3. 代码迁移成本。
后续篇涉及:服务异步化的概念,服务异步化的几种标准实现,服务异步化容器的特色和实现,现有容器可优化的点。