1. 程式人生 > >Spring Boot中嵌入式Servlet容器的比較

Spring Boot中嵌入式Servlet容器的比較

  1. 介紹
    隨著雲原生應用和微服務的流行也催生了對嵌入式Servlet容器需求的增長。為更加簡單的構建應用和服務,Spring Boot為開發者提供了三種成熟的容器:Tomcat,Undertow和Jetty。
    在本文中,我們會演示了一種方法:測量啟動和增加負載時獲取的指標來快速的比較不同容器實現的效能差異。
  2. 依賴
    首先我們在pom.xml中指定了spring-boot-starter-web 這個依賴,這是我們為我們每一個容器實現進行測量前所必須的具備。
    通常的,我們會指定使用 spring-boot-starter-parent 作為我們的父依賴,然後接著加入我們需要的starter:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2.1 Tomcat
因為在我們spring-boot-starter-web在我們的依賴中,預設採用的是Tomcat容器,因此我們不需要再做更多的配置。
2.2 Jetty
為了使用Jetty,我們首先需要從spring-boot-starter-web中去掉spring-boot-starter-tomcat 這個依賴。
然後,我們只需要簡單引入spring-boot-starter-jetty的依賴:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

2.3 Undertow
設定Undertow的方式和Jetty類似,不過去除依賴後,我們會使用spring-boot-starter-undertow 作為我們的依賴:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

2.4 Actuator
我們使用Spring Boot的Actuator元件來對系統進行壓力測試和查詢應用指標。
你可以通過閱讀這篇文章來更加詳細的瞭解Actuator。本文中,我們只需要在pom中新增這個依賴:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2.5 Apache Beach
Apache Bench是一個開源的負載測試工具,但它通常會和Apache Web 伺服器捆綁在一起。
Windows使用者可以點選此處進行下載。如果你的Windows電腦上已經有了這個工具,你應該可以在你的apache/bin 目錄下找到ab.exe 。
如果你是Linux的使用者,你可以通過apt-get命令來安裝ab

$ apt-get install apache2-utils

  1. 啟動指標
    3.1 蒐集
    為了蒐集我們的啟動指標,我們會在Spring Boot的ApplicationReadyEvent 註冊我們關注的指標。
    我們直接使用Actuator元件提供的MeterRegistry工具,通過程式設計的方式來直接獲取我們所關注的指標:

@Component
public class StartupEventHandler {

// logger, constructor

private String[] METRICS = {
"jvm.memory.used",
"jvm.classes.loaded",
"jvm.threads.live"};
private String METRIC_MSG_FORMAT = "Startup Metric >> {}={}";

private MeterRegistry meterRegistry;

@EventListener
public void getAndLogStartupMetrics(
ApplicationReadyEvent event) {
Arrays.asList(METRICS)
.forEach(this::getAndLogActuatorMetric);
}

private void processMetric(String metric) {
Meter meter = meterRegistry.find(metric).meter();
Map<Statistic, Double> stats = getSamples(meter);

    logger.info(METRIC_MSG_FORMAT, metric, stats.get(Statistic.VALUE).longValue());

}

// other methods
}

為了避免人為的通過Actuator的REST端點進行效能指標的查詢,我們啟動一個獨立的JMX程序來記錄應用啟動應用時我們所關注的指標資料。
3.2 選擇
Actuator可以為我們提供了大量的指標資料。在應用啟動後,我們選擇了三個具有代表性的指標,他們可以展現系統執行時的關鍵點的概況。
jvm.memory.used JVM在啟動後總共使用的記憶體量
jvm.classes.loaded JVM中總共載入的class檔案的數量
jvm.threads.live JVM中存活的執行緒數量。在我們的測試中,這個值可以展現為處於"休息"狀態的執行緒的數量。

  1. 執行時指標
    4.1 蒐集
    除了提供啟動指標, 當我們啟動Apache Bench後,使用Actuator元件提供的/metrics端點作為目標url進行請求,以使我們的應用處於負載階段。
    為了測試一個真實的處於負載的應用,我們可能更需要使用我們的應用系統所提供的端點進行測試。
    一旦我們的應用啟動完成,我們使用以下命令來啟動並執行的ab:

ab -n 10000 -c 10 http://localhost:8080/actuator/metrics

4.2 選擇
Apache Bench能夠快速的給予我們一些有用的資訊:包括連線時間,在某一段時間的請求的佔比等。
為了我們的目的,我們通常更加關注每秒請求個數和每個請求的處理時間的均值。

  1. 結果
    在啟動階段,我們通過對Tomcat,Jetty和Undertow所佔用記憶體的比較,我們發現Jetty佔用的記憶體最小,Undertow次之,Tomcat最多。
    在我們的測量中,我麼還能發現Tomcat,Jetty和Undertow的效能比較:我們可以清楚的看到Undertow很明顯是最快的,而Jetty則相對於慢一些。
    MetricTomcatJettyUndertowjvm.memory.used (MB)168155164jvm.classes.loaded986997849787jvm.threads.live251719Requests per second154216271650Average time per request (ms)6.4836.1486.059
    請注意,我們的這些指標都是在裸專案(沒有新增任何業務程式碼的專案)下進行的測量。如果是自己的專案,那麼測量指標大概率會有不同。
  2. 基準測試討論
    開發適當的基準測試以充分測試容器實現的效能可能是相當複雜的。為了提取其中最關鍵的資訊,能夠清晰認識到要針對每個具體問題所編寫出正確的測試案例是非常重要的。
    值得注意的是,示例中使用了Actutor的HTTP GET請求作為負載,以此來收集的所需要指標的測量值。
    可預見的是,不同的工作負載會導致對容器實現的進行不同指標的測量和蒐集。如果需要更加健壯和精確的測量,建立一個更接近生產用例的測試計劃是一個非常好的辦法。
    此外,更復雜的基準測試解決方案(如JMeter或Gatling)可能會得到更有價值的測試結論。
  3. 選擇容器
    選擇一個合適的容器實現應當要基於多方面的考量,而不能僅僅是基於一些硬性指標的概況就做出倉促的選擇。適用性,特性,可配置性和策略通常也起到相當大的考量。
  4. 結論
    在本文中,我們看了Tomcat,Jetty和Undertow的嵌入式的Servlet容器的實現。我們通過Actuator暴露的metrics端點來測試了每一個Servlet容器在使用預設配置時的啟動後的執行時指標。
    我們通過使用Apache Bench來對應用進行壓力測試,同時收集各項效能指標。
    最後,我們談論了這個策略的優勢,並提及了當比較各個實現的基準時應當要熟記於心的幾個建議。和往常一樣,你可以從Github上獲取專案中的所有原始碼。