前言

模板引擎,一直以来,个人都比较喜欢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后,怎么也无法使用,访问页面时后台一直报错,不知道是不是我的使用方法不对:

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条列表记录:

@Controller
public class TestController {

    private List<User> users = new ArrayList<>();

    public TestController() {
        for (int i = 0; i < 100; i++) {
            User user = new User();
            user.setUserId(Long.valueOf(i));
            user.setUsername("username-" + i);
            user.setPassword("123456-" + i);
            user.setEmail(i + "[email protected]");
            user.setMobile("13666666666");
            users.add(user);
        }
    }

    @RequestMapping("index")
    public String index(ModelMap modelMap) {
        modelMap.put("users", users);
        return "index";
    }
}

FreeMarker代码:

<#list users as item>
    <tr>
        <td>${item.userId}</td>
        <td>${item.username}</td>
        <td>${item.password}</td>
        <td>${item.email}</td>
        <td>${item.mobile}</td>
    </tr>
</#list>

Thymeleaf代码:

<tr th:each="item : ${users}">
    <td th:text="${item.userId}"></td>
    <td th:text="${item.username}"></td>
    <td th:text="${item.password}"></td>
    <td th:text="${item.email}"></td>
    <td th:text="${item.mobile}"></td>
</tr>

Enjoy代码:

#for(item : users)
    <tr>
        <td>#(item.userId)</td>
        <td>#(item.username)</td>
        <td>#(item.password)</td>
        <td>#(item.email)</td>
        <td>#(item.mobile)</td>
    </tr>
#end

测试结果

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

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

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
Running 30s test @ http://localhost:8083/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   406.90ms  464.12ms   2.00s    83.35%
    Req/Sec    18.75     26.83   343.00     90.49%
  35301 requests in 30.10s, 553.82MB read
  Socket errors: connect 0, read 223, write 1, timeout 1141
Requests/sec:   1172.60
Transfer/sec:     18.40MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
Running 30s test @ http://localhost:8080/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.51s   409.92ms   2.00s    71.07%
    Req/Sec     1.70      2.73    20.00     91.16%
  2007 requests in 30.10s, 32.75MB read
  Socket errors: connect 0, read 332, write 0, timeout 1651
Requests/sec:     66.68
Transfer/sec:      1.09MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
Running 30s test @ http://localhost:8082/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   390.47ms  431.10ms   2.00s    84.32%
    Req/Sec    19.97     27.40   349.00     90.14%
  41910 requests in 30.11s, 660.88MB read
  Socket errors: connect 0, read 167, write 0, timeout 843
Requests/sec:   1391.92
Transfer/sec:     21.95MB

第二轮:

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
Running 30s test @ http://localhost:8083/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   150.24ms  213.48ms   1.97s    89.05%
    Req/Sec    54.68     25.05   480.00     75.12%
  162974 requests in 30.10s, 2.49GB read
  Socket errors: connect 0, read 153, write 0, timeout 42
Requests/sec:   5413.68
Transfer/sec:     84.80MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
Running 30s test @ http://localhost:8080/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   444.69ms  394.37ms   2.00s    56.10%
    Req/Sec    14.22     17.09   363.00     92.74%
  33532 requests in 30.10s, 529.98MB read
  Socket errors: connect 0, read 256, write 4, timeout 500
Requests/sec:   1113.85
Transfer/sec:     17.60MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
Running 30s test @ http://localhost:8082/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   102.74ms  112.32ms   1.53s    90.77%
    Req/Sec    61.42     25.19   555.00     82.25%
  183754 requests in 30.10s, 2.83GB read
  Socket errors: connect 0, read 134, write 1, timeout 0
Requests/sec:   6104.33
Transfer/sec:     96.25MB

第三轮:

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8083/index
Running 30s test @ http://localhost:8083/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   151.18ms  219.30ms   1.98s    89.11%
    Req/Sec    55.34     25.82   600.00     79.00%
  164058 requests in 30.09s, 2.51GB read
  Socket errors: connect 0, read 240, write 0, timeout 42
Requests/sec:   5452.72
Transfer/sec:     85.41MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8080/index
Running 30s test @ http://localhost:8080/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   348.74ms  310.69ms   2.00s    78.22%
    Req/Sec    16.70     11.52   250.00     78.23%
  46761 requests in 30.10s, 737.67MB read
  Socket errors: connect 0, read 274, write 0, timeout 240
Requests/sec:   1553.55
Transfer/sec:     24.51MB

selflydeMacBook-Pro:~ liyd$ wrk -t100 -c500 -d30s http://localhost:8082/index
Running 30s test @ http://localhost:8082/index
  100 threads and 500 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   107.51ms  116.44ms   1.74s    89.56%
    Req/Sec    59.45     27.25   670.00     81.62%
  177719 requests in 30.10s, 2.74GB read
  Socket errors: connect 0, read 130, write 0, timeout 0
Requests/sec:   5903.36
Transfer/sec:     93.08MB
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类上有以下注释:

@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就是最佳选择。

项目代码

FreeMarker: http://git.oschina.net/selfly/performance-freemarker

Thymeleaf: http://git.oschina.net/selfly/performance-thymeleaf

Enjoy: http://git.oschina.net/selfly/performance-enjoy

Beetl: http://git.oschina.net/selfly/performance-beetl (运行不成功)

你可能感兴趣的内容
2条评论
xiandafu 1年前

我是beetl作者,不知道为何你的beetl没有跑起来,挺遗憾,不过你自己求证了一下所谓Thymeleaf内置模板的国内杜撰,挺好的。如果你自己使用Thymeleaf,你会发现我的那个博客说的都是正确的,还有所谓的原型即页面,也是扯淡

hw_imxy 1年前

嗯你说的对你最牛,Beetl无敌行了吧。我看还是用FreeMarker吧。

selfly

交流QQ群:32261424
Owner