下图是对几个主流的应用服务器使用比率的粗率统计结果做出的一个饼图。这个图的数据也许不够精确,但它还是可以在一定程度上反映我们web项目对各类应用服务器的一些选择趋势。我们可以看到,Tomcat占据了主要的地位,但是它并不孤独,有超过一半以上的应用并没有使用tomcat作为web容器。这是针对每个项目自身特点做出的选择,也许我们无法比较出哪一款是最好的应用服务器,但是,我们可以在众多的应用服务器中,做出一些性能上的测试和比较,选择一款最适合自己的项目的应用服务器。
本次的报告中,我选择了较为受关注的jetty以及稍微冷门一点的glassfish作为研究对象,对它们在windows和linux上分别进行了App项目的部署和简单的测试,希望这个文档能对以后的应用服务器研究提供一些简单的参考。
Jetty是一款由纯JAVA语言编译的实现servlet规范的应用服务器。它是eclipse基金会支持的一款软件。打开它的目录你会发现很有趣的事情,它的启动程序是一个叫start.jar的文件,并且需要在cmd中执行命令:java –jar start.jar。这对于习惯于tomcat等其他以bat(windows)或者sh(linux)文件启动的使用者来说,是一个小小的新体验。
Jetty的很多特性在网络上都有很详细的记载,在文档中我不想做任何的简单复制或者引用,那些资料大家可以去百度搜索。这里只提它的一点最明显的优势:嵌入式服务器。Jetty之所以能占据一个不错的使用比率,也正是因为它的这个特性。它可以在java程序中作为一个servlet让java程序进行调用。这使它更多的是作为两台机器之间的通信服务应用。它的这种特性,可以省去写socket等通信程序的烦恼,在两台机器的那些非标准的servlet程序,直接以http请求的形式,调用jetty方法,形成一个标准的符合servlet规范的通信。当然,它作为web容器的性能也是十分优秀的,后面的测试中,我将会提到。但是,作为一个互联网公司,目前并不存在这方面的逻辑代码需要整改成jetty形式的嵌入服务,我并没有对它怎么嵌入使用去做更进一步的研究。
这是一个让我觉得使用起来十分便捷人性的应用服务器,最开始选择研究glassfish的原因,是因为它是一款开源的相对于tomcat对热部署支持的十分好的应用服务器。因此,我也将它集成到了eclipse中,进行热部署的测试。如果开发环境的应用服务器可以换成glassfish的话,也许可以节约很多由于反复重启tomcat而浪费的时间。
它是执行一个叫asadmin.bat的文件进行启动。在执行asadmin.bat后会弹出一个命令框,我们在里面输入start-domain domain1(这个名字跟你自己的domains下的domain目录名有关)
启动后,我们在地址栏输入localhost:4848就可以进入它的管理界面了。
通过它的这个界面可以对项目进行部署,还有数据库连接等进行管理还有集群等配置,甚至还有对数据和项目运行情况的监控管理,相对于tomcat,glassfish的这个功能可以减少使用者的使用难度,更加直观地配置应用服务器,更好的优化应用服务器的性能。Glassfish简单的服务器性能比较,将在后面中提到。
本次测试将分别在windows和linux环境下进行测试。测试将在两个环境中使用的是开发环境,数据库连接的是开发库,测试项目为自研APP。操作系统使用的是windows7旗舰版和centos7 server。计算机的基本情况如下图所示:
使用的压力测试软件均为:JMeter
测试所使用的请求地址连接为:
/App/users/getUserInfo?uid=11001&v=1.0.0
/App/uses/doLogin?uName=test&pwd=pass@123
/App/menus/getModuleMenu?uid=11001&urole=0&uSource=0
测试将随机访问这三个请求。三个请求分别为云服务的相关请求。三个请求对应三种不同类型的服务,具有很强的业务代表性。
在测试结果中我们将看到几个参考值,下面给出每个值的意义。
样本数目:总共发送到服务器的请求数。
最新样本:代表时间的数字,是服务器响应最后一个请求的时间。
吞吐量:服务器每分钟处理的请求数。
平均值:总运行时间除以发送到服务器的请求数。
中间值:时间的数字,有一半的服务器响应时间低于该值而另一半高于该值。
偏离:服务器响应时间变化、离散程度测量值的大小,或者,换句话说,就是数据的分布,这个值越小越好。
使用jdk版本:1.7.0_80
100线程测试(1)
测试的端口为8090,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为35164ms
100线程测试(2)
测试端口为8090,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒钟建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为35473ms
100线程测试(3)
测试端口为8090,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是立即建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为32037ms
50线程测试(1)
测试端口为8090,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为23171ms
10线程测试(1)
测试端口为8090,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为5675ms
100线程测试(1)
测试的端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为25126ms
100线程测试(2)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为23848ms
100线程测试(3)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是有100个线程同时发起请求。下面是测试结果:
可以看到请求平均处理响应时间为27552ms
50线程测试(1)
测试端口为8080,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为12836ms
10线程测试(1)
测试端口为8090,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为7277ms
100线程测试(1)
测试的端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为39581ms
100线程测试(2)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为32685ms
100线程测试(3)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是有100个线程同时发起请求。下面是测试结果:
可以看到请求平均处理响应时间为33888ms
50线程测试(1)
测试端口为8080,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为26328ms
10线程测试(1)
测试端口为8090,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为4334ms
此外,在3个应用服务器测试期间,由于同时处理的请求数过多,均发生了如下异常,提示创建的对象过多,内存不足。这跟机器环境,应用服务器设置以及JVM设置有关,但也有可能我们的APP也许隐藏着某些对象没有被垃圾回收机制回收的问题(只是猜测)。
使用JDK版本1.7.0_25
请求地址为192.168.32.129
100线程测试(1)
测试的端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为28351ms
100线程测试(2)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒钟建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为29166ms
100线程测试(3)
测试端口为8080,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是立即建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为28267ms
50线程测试(1)
测试端口为8080,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为20784ms
10线程测试(1)
测试端口为8080,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为3139ms
100线程测试(1)
测试的端口为8088,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为28697ms
100线程测试(2)
测试端口为8088,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为28260ms
100线程测试(3)
测试端口为8088,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是有100个线程同时发起请求。下面是测试结果:
可以看到请求平均处理响应时间为28675ms
50线程测试(1)
测试端口为8088,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为26809ms
10线程测试(1)
测试端口为8088,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为6110ms
100线程测试(1)
测试的端口为8888,线程数为100,循环次数为10线程间请求的允许的间隔时间为10,也就是10秒钟内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为30078ms
100线程测试(2)
测试端口为8888,线程数为100,循环次数为10线程间请求的允许的间隔时间为5,也就是5秒内建立100个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为32510ms
100线程测试(3)
测试端口为8888,线程数为100,循环次数为10线程间请求的允许的间隔时间为0,也就是立即有100个线程同时发起请求。下面是测试结果:
可以看到请求平均处理响应时间为30871ms
50线程测试(1)
测试端口为8888,线程数为50,循环次数为20线程间请求的允许的间隔时间为0,也就是立即建立50个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为25443ms
10线程测试(1)
测试端口为8888,线程数为10,循环次数为100线程间请求的允许的间隔时间为0,也就是立即建立10个线程发起请求。下面是测试结果:
可以看到请求平均处理响应时间为2745ms
在第2章给出了丰富的测试数据。测试的重点以高并发的情况为主,也就是100线程的情况,分别每个应用服务器都做了三次测试。我们对比这些测试数据,可以看出无论是在windows还是在linux环境下,glassfish对高并发的处理比jetty和tomcat都要好一些,jetty与tomcat对高并发的处理能力相比相差不大。而在50线程并发请求的时候,我们发现这个差距缩小了,直到在10线程这种低并发请求的情况中,三个应用服务器的处理性能却又反了过来。我们可以清楚地看到,tomcat的处理能力比jetty更好,而glassfish的处理能力变得较弱。与此同时,我们还可以看到相对于windows7,CentOS7server中应用服务器的表现更加出色,响应时间更短,即使centos7server只是运行在虚拟机之中。
应用服务器的性能需要参考的数据远远不是一个并发请求响应时间能解决的,还有对jetty与glassfish的部署与设置,每个应用服务器的标准都不同,例如jetty对servlet标准的严格检查,令我们在glassfish和tomcat下能运行的项目,在jetty中都不能部署起来。例如glassfish,对高并发的情景处理的较好,拥有一个十分强大的项目和服务器管理程序,而这以为的优点却也换来了处理一般情况的能力却不如其他应用服务器的缺点。又如我在第一章所介绍的,为什么处在中庸的jetty在2014年统计的应用服务器选择上,占有率能排第二,因为它的特点更加地聚焦于成为一个嵌入式的服务器,将一些非原本需要通过socket或者其他方式连接的非web业务逻辑封装成一个servlet。结合实际来说就像我们正在使用的上传编译工具,有一部分文件处理和调用cmd命令的代码便是用socket与我们的java服务端连接,这种形式的连接,使用jetty便可以轻松的替换,并且不用考虑更多的字符编码问题,以及socket的编写和参数的处理。这次的研究,对于三种应用服务器无法评价出哪一款更适合我们的APP,因为还有很多的因素需要去参考。例如,稳定性,安全性,伸缩性等等。
最后,我需要说明的是,这些测试数据只能提供一个简单的参考。这些数据是在本地开发环境的数据库与我自己的电脑环境得到的,因此这些结论只能当作一个趋势的参考。真正生产环境的服务器性能测试也许会和本次测试有出入,并且如果要正真的模拟服务器的整个工作,我们所选取的随机请求也不能只是三个有代表性的连接,而应该是通过实际环境每个功能被请求的次数,来模拟真正的服务器性能测试,以达到一个更为精确的结果,让我们可以从中来调优我们的应用服务器,以及选取更好的server环境。