Quantcast
Channel: 涂0实验室 » cluster
Viewing all articles
Browse latest Browse all 4

一次php和java的性能比拼

$
0
0

因工作上的需要,我要对比,在同一台物理机上,php和java的性能差异。两个测试程序的功能完全一样,都是非常简单,只涉及到http请求的处理,redis的一次读写和数据库的一次读写。物理机有16核cpu,48G内存,硬盘空间足够。测试结果数据如下:

被测方案虚拟用户数GET平均响应时间(秒)UPDATE平均响应时间(秒)每秒点击数
php5000.0560.0727759
10000.1950.1667471
15000.3180.2707471
单个jvm5000.1220.0525330
集群jvm5000.0580.0588500
10000.1240.1278062
15000.1930.1937763
20000.2650.2677656
23990.3180.3207593
30000.3960.3987488
40000.5510.5537338
50000.6600.6647542

1、单个jvm

理论上来说java的性能应当是高于php的,但是结果很让人失望,无论我如何加大压力,在单个jvm的时候,java都达不到每秒7700请求的指标。我首先想到的是对tomcat做性能调优,我先后使用了以下措施:

  • 增大jvm内存 -Xmx1578m
  • 使用tomcat nio http connector
  • 增大tomcat处理请求的线程数量,maxThreads=2000,processorCache=2000,acceptCount=10000
  • 增大jedis连接池和c3p0数据库连接池到2000
  • 不使用数据库连接池
  • 不使用jedis连接池
  • 不使用spring mvc直接使用servlet

以上措施中只有不使用jedis连接池少量的提高了每秒请求数和响应时间,其他的措施没有多少改进,而且不用数据库连接池时,性能急剧下降,每秒请求数下降到不足之前的五分之一。为了找出性能瓶颈,找出原因,我用jprofiler分析,发现了一些问题。

1.1 commons-pool在并发时性能不佳

jedis的对象池是用commons-pool实现的,这个实现在多线程,并发情况下反倒成了性能瓶颈之一。当2000线程处理请求时,每个线程都要先锁定一个对象才能获取或归还jedis对象,导致这些线程在这个地方排队,性能降低。后来改成每个请求都重新打开一次jedis链接后,性能反倒有一点提升。

1.2 数据库连接池让人又爱又恨

不用数据库连接池是万万不行的,每次打开数据库连接,性能太差。相比之下php没有使用连接池,性能依旧很好,估计是jdbc实现的问题。使用了数据库链接池后,c3p0和commons-pool一样,在并发时,也能成了性能瓶颈,好多线程都在等待获得c3p0的锁。

1.3 诡异的线程集体空闲,集体繁忙

从jprofiler的线程运行图里能看到,tomcat的线程大部分时间不是在处理请求,而是在莫名其妙的空闲,代码停在了tomcat使用的jre自带的ThreaderExecutor一个方法上。而且,这些线程往往同时空闲持续近10秒的时间,然后突然繁忙起来,去争抢数据库的锁,运行,然后再次先后都陷入空闲。load runner的每秒请求数曲线和响应时间曲线呈现出巨大的波动,一会每秒点击高达10000,一会跌入2000以下。而从jprofiler看出,jvm的内存空间使用不超过500m,而且波动很平稳,gc表现稳定,没有大量的全垃圾收集(full gc)。

1.4 apr也打破不了僵局

因此,我怀疑是不是jvm在处理大量并发tcp连接的时候,性能不好,导致这样的局面。为此,我想到了使用tomcat native。它可以让tomcat使用c语言实现的本地代码库来处理tcp连接,而这个库也是apache httpd使用的,性能应当相当好,至少在处理网络方面应当很好吧。编译安装成功后,测试数据显示,并没有多少改变,很让人沮丧。那些线程还是那样的间歇性地集体空闲,集体繁忙。

1.5 jetty和glassfish的表现

看来问题可能处在tomcat自身,于是我用jetty和glassfish做了测试。jetty的表现和tomcat一样,一样的性能指标,一样诡异的线程。glassfish的性能指标比tomcat稍微差一点,但是它的每秒请求数和响应时间比较稳定,波动不大,很稳定,这一点比tomcat和jetty好多了。用jprofiler看了一下glassfish只有5个线程在处理http请求,而且这些线程都一直处于繁忙状态,不会像tomcat和jetty,众多线程间歇性集体空闲集体繁忙,不均匀。由此我想到,是不是线程配置得太多了,导致tomcat和jetty调度不过来,才会出现那样的诡异的现象。

1.6 tomcat的最佳线程数

我测试了不同线程数的时候tomcat的表现。和glassfish同样5个线程时,tomcat表现很糟糕,逐渐增大tomcat线程数量到50后,性能指标达到了2000线程时的性能指标,再加上去,性能变化不大。看来tomcat默认的200线程数量是很有道理的,200可能是目前大部分机器的最优配置了。

1.7 单个jvm的极限

无论怎么调线程数量,换什么样的服务器,单个jvm最多也只能达到每秒处理5300请求。看来一个jvm是有性能极限的,很可能不能完全的发挥硬件的能力。

2 集群

集群方案是这样的,前端一个apache,使用mod_jk把请求转发到后端的多个tomcat或glassfish。apache, tomcat和glassfish自然是在同一台物理机上的。由于tomcat和jetty的实现机制太像了,就没有单独测试jetty。而tomcat和glassfish在集群时性能表现一样,没有明显的差别。而且,从2个jvm逐步增大到6个,性能表现也没有明显差别。使用apache在前端转发后,tomcat也像glassfish一样变得平稳了,估计这得归功与ajp协议,和mod_jk的连接复用。

2.1 性能分析

集群后java的性能表现终于超越了php。随着压力的增大,java的响应时间增长比php缓慢。在相同的响应时间下,java每秒处理了更多的请求,峰值时多处理近10%。相同响应时间时,java能承受更大的压力,如果以虚拟用户数量来衡量,能多承受50%的压力。

无论php和java,随着压力的增大,请求响应时间和每秒请求数快速上升,但是到达一个点后,压力再增大,每秒请求数会缓慢的下降,而请求响应时间会缓慢的上升。

3 结论

3.1 单个大jvm不如多个小jvm

一个线程很多,内存很大的jvm并不能完全发挥硬件的性能,还不如多个内存小,线程少的多个jvm集群。尽管多个小jvm总体上用了更多的内存,但是每个jvm内的线程少,意味着jvm调度的复杂性减小,竞争同一个锁的线程数更少。

3.2 一定要用数据库连接池

如果不用数据库连接池,java的表现太差了。看来mysql的jdbc driver比它的php用的c语言实现重得多。

3.3 tomcat jetty glassfish

测试中使用的是tomcat7 jetty8和glassfish3.2三者的性能表现差不多,而glassfish更加有好的使用界面,更加稳定的性能数据,使它表现得更出色。

3.4 java的性能还是比php好的

在相同的硬件下,如果单个jvm不能完全发挥硬件的性能,通过多个jvm的集群可以,而且会超过php。

我想,在这个测试例子中,因为每次打开连接执行一次数据库操作,java的数据库连接池的优势没有完全体现出来,否则java应该会表现得更好。如果是cpu计算密集型的任务,java的性能应该会更好。


Viewing all articles
Browse latest Browse all 4

Latest Images

Trending Articles





Latest Images