FreeMarker、Thymeleaf、Enjoy 模板引擎性能测试

前言

模板引擎,一直以来,个人都比较喜欢velocity,只是这货差不多7年没有更新,虽然前几天抽风似的发布了个2.0版本,但7年的脚步已经落后了。

后来看到Thymeleaf挺不错,个人项目中也有在使用,这不在osc看到一篇文章:关于Thymeleaf的真相 和Thymeleaf开撕上了,最大的糟点应该是性能问题,看来有必要自己做个性能测试来验证下,毕竟下一步还打算在公司正式项目中使用Thymeleaf呢。

声明

本测试仅针对本人个人使用情况,其使用场景、工具、硬件等均有可能对测试结果造成影响。

测试结果只是对本次测试做的一个记录,仅供参考,并不能说明引擎本身的好坏。

在对测试结果总结及模板引擎的选择上,难免会有个人的想法及主观意识,这只是个人选择喜好并不代表对引擎好坏的评判,请勿对号入座。

候选模板引擎

项目中以使用Spring Boot为主,所以测试的模板引擎自然要与其方便整合使用为上。

首先当然是官方提供默认集成的模板引擎了,在使用Spring Initializr工具创建Spring Boot项目时,发现新版本的Spring Boot已经不推荐使用velocity了(其实从spring 4.3开始就不推荐使用了),见下图:

模板引擎选择

这样一来官方提供默认集成的就只剩下FreeMarker和Thymeleaf了,听说Thymeleaf从3.0开始性能已大幅提高和FreeMarker相当了,刚好趁这次可以实际对比下。

Enjoy,JFinal出品的模板引擎,可脱离JFinal单独使用,项目地址:http://git.oschina.net/jfinal/enjoy

Beetl,上面文章中和Thymeleaf的开撕对象,项目地址:http://git.oschina.net/xiandafu/beetl2.0 本来想把它也加进去的,只可惜我按照他的官方文档 Spring Boot集成一节中的配置集成starter后,怎么也无法使用,访问页面时后台一直报错,不知道是不是我的使用方法不对:

 
 
  1. javax.servlet.ServletException: Circular view path [index]: would dispatch back to the current handler URL [/index] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

所以到最后测试的模板引擎只有FreeMarkerThymeleafEnjoy这三个了,其它的就不考虑了,毕竟项目中要使用测了才有意义。

测试流程

因为使用Spring Boot,所以都是Spring Boot项目。

Thymeleaf号称使用SpringEL比使用ognl更快,这样对他也算公平。只是Spring Boot到现在默认集成的都是Thymeleaf 2.x版本,将版本号改成3.x版本之后不知道是否有影响。

配置文件中缓存配置统一设置为true,因为在Spring Boot中不使用DevTool的情况下Thymeleaf默认就是true开启缓存的,而FreeMarker默认是false不开启缓存的,当然Enjoy就没有这个配置了,在代码中把DevMode设置为false。

都使用Spring Boot的main方式启动项目,JVM参数统一设置为:-Xms512m -Xmx512m

为避免web容器的启停造成的内存波动,重启系统后打开所有项目并启动容器后再进行测试,三个项目使用三个不同的端口。这里不计算项目启动本身内存的占用。

启动项目后浏览器先进行一次访问,让项目完成第一次的编译加载。

测试工具

上面的文章提到使用了TEB

这里就换一个工具测测,使用wrk来测试web项目的实际吞吐量。

测试代码

这里列出主要代码,下面附有各个项目的下载地址,可下载后查看其它配置项。

Controller代码,渲染100条列表记录:

 
 
  1. @Controller
  2. public class TestController {
  3. private List<User> users = new ArrayList<>();
  4. public TestController() {
  5. for (int i = 0; i < 100; i++) {
  6. User user = new User();
  7. user.setUserId(Long.valueOf(i));
  8. user.setUsername("username-" + i);
  9. user.setPassword("123456-" + i);
  10. user.setEmail(i + "[email protected]");
  11. user.setMobile("13666666666");
  12. users.add(user);
  13. }
  14. }
  15. @RequestMapping("index")
  16. public String index(ModelMap modelMap) {
  17. modelMap.put("users", users);
  18. return "index";
  19. }
  20. }

FreeMarker代码:

 
 
  1. <#list users as item>
  2. <tr>
  3. <td>${item.userId}</td>
  4. <td>${item.username}</td>
  5. <td>${item.password}</td>
  6. <td>${item.email}</td>
  7. <td>${item.mobile}</td>
  8. </tr>
  9. </#list>

Thymeleaf代码:

 
 
  1. <tr th:each="item : ${users}">
  2. <td th:text="${item.userId}"></td>
  3. <td th:text="${item.username}"></td>
  4. <td th:text="${item.password}"></td>
  5. <td th:text="${item.email}"></td>
  6. <td th:text="${item.mobile}"></td>
  7. </tr>

Enjoy代码:

 
 
  1. #for(item : users)
  2. <tr>
  3. <td>#(item.userId)</td>
  4. <td>#(item.username)</td>
  5. <td>#(item.password)</td>
  6. <td>#(item.email)</td>
  7. <td>#(item.mobile)</td>
  8. </tr>
  9. #end

测试结果

总共三轮,下面是结果,可以参考看看。

第一轮,从上到下依次为FreeMarker、Thymeleaf、Enjoy:

 
 
  1. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
  2. Running 30s test @ http://localhost:8083/index
  3. 100 threads and 500 connections
  4. Thread Stats Avg Stdev Max +/- Stdev
  5. Latency 406.90ms 464.12ms 2.00s 83.35%
  6. Req/Sec 18.75 26.83 343.00 90.49%
  7. 35301 requests in 30.10s, 553.82MB read
  8. Socket errors: connect 0, read 223, write 1, timeout 1141
  9. Requests/sec: 1172.60
  10. Transfer/sec: 18.40MB
  11. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
  12. Running 30s test @ http://localhost:8080/index
  13. 100 threads and 500 connections
  14. Thread Stats Avg Stdev Max +/- Stdev
  15. Latency 1.51s 409.92ms 2.00s 71.07%
  16. Req/Sec 1.70 2.73 20.00 91.16%
  17. 2007 requests in 30.10s, 32.75MB read
  18. Socket errors: connect 0, read 332, write 0, timeout 1651
  19. Requests/sec: 66.68
  20. Transfer/sec: 1.09MB
  21. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
  22. Running 30s test @ http://localhost:8082/index
  23. 100 threads and 500 connections
  24. Thread Stats Avg Stdev Max +/- Stdev
  25. Latency 390.47ms 431.10ms 2.00s 84.32%
  26. Req/Sec 19.97 27.40 349.00 90.14%
  27. 41910 requests in 30.11s, 660.88MB read
  28. Socket errors: connect 0, read 167, write 0, timeout 843
  29. Requests/sec: 1391.92
  30. Transfer/sec: 21.95MB

第二轮:

 
 
  1. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
  2. Running 30s test @ http://localhost:8083/index
  3. 100 threads and 500 connections
  4. Thread Stats Avg Stdev Max +/- Stdev
  5. Latency 150.24ms 213.48ms 1.97s 89.05%
  6. Req/Sec 54.68 25.05 480.00 75.12%
  7. 162974 requests in 30.10s, 2.49GB read
  8. Socket errors: connect 0, read 153, write 0, timeout 42
  9. Requests/sec: 5413.68
  10. Transfer/sec: 84.80MB
  11. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
  12. Running 30s test @ http://localhost:8080/index
  13. 100 threads and 500 connections
  14. Thread Stats Avg Stdev Max +/- Stdev
  15. Latency 444.69ms 394.37ms 2.00s 56.10%
  16. Req/Sec 14.22 17.09 363.00 92.74%
  17. 33532 requests in 30.10s, 529.98MB read
  18. Socket errors: connect 0, read 256, write 4, timeout 500
  19. Requests/sec: 1113.85
  20. Transfer/sec: 17.60MB
  21. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
  22. Running 30s test @ http://localhost:8082/index
  23. 100 threads and 500 connections
  24. Thread Stats Avg Stdev Max +/- Stdev
  25. Latency 102.74ms 112.32ms 1.53s 90.77%
  26. Req/Sec 61.42 25.19 555.00 82.25%
  27. 183754 requests in 30.10s, 2.83GB read
  28. Socket errors: connect 0, read 134, write 1, timeout 0
  29. Requests/sec: 6104.33
  30. Transfer/sec: 96.25MB

第三轮:

 
 
  1. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
  2. Running 30s test @ http://localhost:8083/index
  3. 100 threads and 500 connections
  4. Thread Stats Avg Stdev Max +/- Stdev
  5. Latency 151.18ms 219.30ms 1.98s 89.11%
  6. Req/Sec 55.34 25.82 600.00 79.00%
  7. 164058 requests in 30.09s, 2.51GB read
  8. Socket errors: connect 0, read 240, write 0, timeout 42
  9. Requests/sec: 5452.72
  10. Transfer/sec: 85.41MB
  11. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
  12. Running 30s test @ http://localhost:8080/index
  13. 100 threads and 500 connections
  14. Thread Stats Avg Stdev Max +/- Stdev
  15. Latency 348.74ms 310.69ms 2.00s 78.22%
  16. Req/Sec 16.70 11.52 250.00 78.23%
  17. 46761 requests in 30.10s, 737.67MB read
  18. Socket errors: connect 0, read 274, write 0, timeout 240
  19. Requests/sec: 1553.55
  20. Transfer/sec: 24.51MB
  21. selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
  22. Running 30s test @ http://localhost:8082/index
  23. 100 threads and 500 connections
  24. Thread Stats Avg Stdev Max +/- Stdev
  25. Latency 107.51ms 116.44ms 1.74s 89.56%
  26. Req/Sec 59.45 27.25 670.00 81.62%
  27. 177719 requests in 30.10s, 2.74GB read
  28. Socket errors: connect 0, read 130, write 0, timeout 0
  29. Requests/sec: 5903.36
  30. Transfer/sec: 93.08MB
  31. selflydeMacBook-Pro:~ liyd$

可以看到第一轮跟后面两轮性能相差很大,这是因为JVM本身会对内存缓存等进行优化,这是预料之中的。

但这对每个引擎都是公平的,横向对比来看,Enjoy最出色,Thymeleaf虽然号称3.0开始已与FreeMarker相当但实际情况还是相差很大。

打的包大小

性能是很重要的一个方面,但打出来的包大小也不可忽视,这从某种方面来说也反应出项目的质量。

下面列出Spring Boot下各个引擎打出的包大小,大小单位显示是在mac下跟windows会有所不同:

FreeMarker:16.1M,单纯FreeMarker的jar没什么第三方依赖,大小在1.5M左右,能够接受。

Thymeleaf:20.7M,Thymeleaf会有groovy等依赖,对一个模板引擎来说应该是超大了。

Enjoy:14.6M,这个不用多说了,JFinal系列出品,简洁著称。

从上面可以看出,打出来的包大小区别还是比较大的,Enjoy非常优秀,FreeMarker正常范围,Thymeleaf就有点夸张了。

Thymeleaf网上很多文章都宣称是轻量级模板引擎,可是看着它打出的包实在感觉轻量不起来。

官方推荐的模板引擎?

为什么要扯这个?因为这多少会对模板引擎的选择产生一些主观的意向,至少我之前有。

网上一直在传Thymeleaf是Spring Boot官方推荐的模板引擎,之前我也一直被此误导。

在此次测试之后我特地到spring的官方网站查找了下,实在是没找到任何推荐的话语,只是列出了几个提供快速集成的引擎选择,Thymeleaf是其中之一。所以这应该只是小道消息。

如果一定要说Spring Boot的官方推荐,那么我在查看Spring的源代码时,发现不推荐使用的VelocityViewResolver类上有以下注释:

 
 
  1. @deprecated as of Spring 4.3, in favor of FreeMarker

官方在注释中推荐使用的是FreeMarker

选择

从上面的测试可以看出,在不考虑模板引擎特有功能的情况下,Enjoy都是完胜。但是在选择时还是要综合考虑其它元素。

在之前我使用Thymeleaf比较多,在我的Spring Security系列文章中也可以看出来,以后应该会使用FreeMarker居多,主要原因有三点:

  1. 性能能够接受。虽然不是最出色,但也不会拖后腿,历经这么多年的考验足以说明问题。
  2. Spring Boot官方提供默认集成。在一个保守的传统金融行业公司中,是一条重量级的说服理由。
  3. IDE支持。特别是在团队中,idea对FreeMarker的支持堪称完美,提供了各种方便。

但是在一些个人项目及工具中应该会使用Enjoy,例如代码生成工具Enjoy就是最佳选择。

猜你喜欢

转载自blog.csdn.net/belalds/article/details/81041959