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後,怎麼也無法使用,訪問頁面時後臺一直報錯,不知道是不是我的使用方法不對:

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.)

所以到最後測試的模板引擎只有FreeMarker、Thymeleaf、Enjoy這三個了,其它的就不考慮了,畢竟項目中要使用測了纔有意義。

測試流程

因爲使用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居多,主要原因有三點:

性能能夠接受。雖然不是最出色,但也不會拖後腿,歷經這麼多年的考驗足以說明問題。
Spring Boot官方提供默認集成。在一個保守的傳統金融行業公司中,是一條重量級的說服理由。
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 (運行不成功)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章