本文将对 openjdk 的发行版:zulu jdk、JetBrains runtime、Amazon corretto、AdoptOpenJDK 以及 Dragonwell 的性能进行全方位比较,使用 cadvisor 收集 docker 的性能数据,然后存储到 prometheus 最后由 grafana 呈现出来。
首先,需要监控 docker 的性能数据,这部分请参考之前的文章:grafana + prometheus + cadvisor 监控 docker,本文书接上回,在上一篇,我已经说过了 如何监控 docker 的性能数据,并且还给了一个截图,现在,我假设你已经知道如何 监控 docker 的性能数据,接下来要做的 就是 部署多个 应用,然后进行压测,最后查看压测数据以及性能数据。
1、新建一个 spring boot 版的 hello world,我的代码大致如下:
@RestController @RequestMapping("/test") public class TestController { private int count = 1; private static final Logger logger = LoggerFactory.getLogger(TestController.class); @RequestMapping("/test") public Map<String, Object>test() { Map<String, Object> map = new HashMap<>(); map.put("message", "success"); map.put("count", count); count++; logger.info("result is {}", map); return map; } }
2、新建 6 个 docker,命令如下:
docker run -m 1024m --name zulujdk -v /home/android/test/zulujdk:/var/apps -p 8100:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh docker run -m 1024m --name JetBrains -v /home/android/test/JetBrains:/var/apps -p 8110:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh docker run -m 1024m --name awsCorretto -v /home/android/test/awsCorretto:/var/apps -p 8120:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh docker run -m 1024m --name aliopenjdk -v /home/android/test/aliopenjdk:/var/apps -p 8130:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh docker run -m 1024m --name aliopenjdk2 -v /home/android/test/aliopenjdk2:/var/apps -p 8140:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh docker run -m 1024m --name AdoptOpenJDK -v /home/android/test/AdoptOpenJDK:/var/apps -p 8150:8080 -itd ubuntu:18.04 /bin/bash /var/apps/startup.sh
这里,你需要将 /home/android/test 的路径换为你自己的实际路径,在 test 目录下,我有 6个对应工作目录,每个工作目录结构一致,分别为 jdk,startup.sh 以及 jar 包
3、startup.sh 如下:
#!/bin/bash cd /var/apps/ rm -rf ./nohup.out rm -rf ./log.txt ./jdk/bin/java -Xms1024m -Xmx1024m -jar ./openjdk-0.0.1-SNAPSHOT.jar >./log.txt 2>&1
4、压测命令:
#!/bin/bash calledAt=$(date +"%Y-%m-%d %H:%M:%S") echo 'called at '$calledAt' sleep 10m now...' sleep 5m beginAt=$(date +"%Y-%m-%d %H:%M:%S") echo 'begin at '$beginAt for i in {0..4}; do url='http://openjdk.kpromise.top/test/test' wrk -t1000 -c10000 -d2s $url sleep 5s done echo -e 'warm up finish...\n\n\n' sleep 30s now=$(date +"%Y-%m-%d %H:%M:%S") echo 'begin local test '$now for port in {0..5}; do for index in {0..4}; do url='http://localhost:81'$port'0/test/test' wrk -t1000 -c10000 -d2s $url sleep 5s done done endAt=$(date +"%Y-%m-%d %H:%M:%S") echo 'endAt '$endAt
5、你可能已经注意到了,这里 aliopenjdk 有两个,这两个分别为 正常使用(对应 aliopenjdk),以及使用 JWarmup 技术(对应aliopenjdk2),后者是先 JWarmup 了一下,前者没有。
Dragonwell JWarmup spring boot 应用
至于 JWarmup 就是先启动一个 docker 不过,startup.sh 就如下了:
#!/bin/bash cd /var/apps/ rm -rf ./nohup.out rm -rf ./log.txt ./jdk/bin/java -Xms1024m -Xmx1024m -XX:-ClassUnloading -XX:-CMSClassUnloadingEnabled -XX:-ClassUnloadingWithConcurrentMark -XX:CompilationWarmUpLogfile=./jwarmup.log -XX:+CompilationWarmUpRecording -XX:CompilationWarmUpRecordTime=300 -jar ./openjdk-0.0.1-SNAPSHOT.jar >./log.txt 2>&1
然后对这个 容器 进行压测,大概5分钟后 生成 jwarmup.log 文件,然后删除 docker 容器,并修改 startup.sh 如下:
#!/bin/bash cd /var/apps/ rm -rf ./nohup.out rm -rf ./log.txt ./jdk/bin/java -Xms1024m -Xmx1024m -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:CompilationWarmUpLogfile=./jwarmup.log -XX:CompilationWarmUpDeoptTime=0 -jar ./openjdk-0.0.1-SNAPSHOT.jar >./log.txt 2>&1
JWarmup spring boot 应用的流程等于是 先在 测试服 或者预发布环境(如果有)上生成文件,然后在正式环境使用这个文件。生成文件你需要在 java 命令后 添加如下参数:
-XX:-ClassUnloading -XX:-CMSClassUnloadingEnabled -XX:-ClassUnloadingWithConcurrentMark -XX:CompilationWarmUpLogfile=jwarmup.log -XX:+CompilationWarmUpRecording -XX:CompilationWarmUpRecordTime=300
使用这个文件(正式环境),你需要在 java 命令后添加如下参数:
-XX:+CompilationWarmUp -XX:-TieredCompilation -XX:CompilationWarmUpLogfile=jwarmup.log -XX:CompilationWarmUpDeoptTime=0
如果需要自定义 JVM 的编译时机,你需要调用:
com.alibaba.jwarmup.JWarmUp.notifyApplicationStartUpIsDone()
具体请参考:dragonwell8 JWarmup 官方文档
我的测试结果大致如下:
压测结果大致如下图:
第一、二列是每秒能处理的请求数,即 RPS ( requests per second ) ,其中,第一列数据是去掉最大值、最小值后求的平均值,第二列数据是全部数据求的平均值,第三列是 openjdk 发行版名称,第四列是 当前的 内存占用,第五列是平均内存占用,最后一列是平均 cpu 占用。出乎意外的是 Dragonwell 完胜其余 几个,差距较大,而其余几个 openjdk 差距则很小,之所以用出乎意料是因为之前用 ab 压测时 zulu openjdk 完胜,Dragonwell 则占用更多内存但是并发却低,考虑到 ab 压测有点 out,而 wrk 则更加现代化,另外,ab 压测 测试出来的数据偏低,测试期间 cpu 占用也比 wrk 低。鉴于两次结果不一致,我又测试了一次,这次的脚本如下:
#!/bin/bash beginAt=$(date +"%Y-%m-%d %H:%M:%S") echo 'begin at '$beginAt for port in {0..5}; do for index in {0..2}; do url='http://localhost:81'$port'0/test/test' wrk -t1000 -c10000 -d2s $url sleep 5s done done endAt=$(date +"%Y-%m-%d %H:%M:%S") echo 'endAt '$endAt
cpu、内存占用如下
并发测试结果请查看:https://cdn.kpromise.top/blog/openjdk1024-result.txt ,整体而言,Dragonwell 并发性能明显优于其余 四个,其中,使用 JWarmup 后 并发更好,但是 cpu、内存占用 也别指望会更低,可能随着时间会变得更大。当内存已经达到最大值(我限制为1G)后,Dragonwell 的并发性能依旧优于其余四个。