異步 Servlet 和同步 Servlet 的性能測試

前言

最近在看 Servlet 3 的異步特性,在網上看了一些文章,有些不解,遂作了一些測試,測試的模擬場景:Web 容器工做線程較少,接口邏輯複雜耗時數據庫

這個場景是大部分的業務場景,例如:Tomcat 的工做線程是 200,業務接口須要遠程 RPC 調用,或者訪問數據庫進行復雜計算等耗時操做。tomcat

因此,仍是比較有參考價值的。服務器

如何測試?

工具:idea,jmeter。app

服務端 idea:SpringBoot 默認容器 Tomcat,修改線程數爲 2,沒錯就是 2,爲了放大工做線程較少的 場景。同時,準備兩個接口:異步接口和同步接口,分別模擬耗時 500ms。異步

代碼以下:async

1, 異步接口ide

@ResponseBody
@Controller
@RequestMapping("async")
public class AsyncTestDemo {

  static ExecutorService workerPool = Executors.newFixedThreadPool(1000);

  @RequestMapping("async")
  public DeferredResult<String> async() {
    DeferredResult<String> defer = new DeferredResult<>((long) 120000);
    workerPool.submit(() -> {
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      defer.setResult("hello async");
    });
    return defer;
  }
}

2, 同步接口工具

@ResponseBody
@Controller
@RequestMapping("sync")
class SyncTestDemo{

  static ExecutorService workerPool =  Executors.newFixedThreadPool(1000);

  @RequestMapping("/sync")
  public String sync() throws ExecutionException, InterruptedException {
    Future<String> future = workerPool.submit(() ->
    {
      Thread.sleep(500);
      return "hello sync";
    });
    return future.get();
  }
}

3, Tomcat 配置:性能

server.port=8081
server.tomcat.max-threads=2

客戶端 jmeter:開啓 200 線程分別訪問兩個接口。獲得聚合報告測試

線程組設置:200 線程

異步請求設置

同步請求設置

聚合報告

從聚合報告能夠看出,二者差距巨大。 平均響應時間相差 45 倍,90% 的響應時間相差 73 倍,99% 的響應時間相差 66 倍。

同步 Servlet 只有在最初的 2 個請求中,響應時間在 500ms 左右,以後,因爲線程數不夠,後面的請求開始線性延時,最大響應時間爲 44 秒(最後一個請求)。同時,錯誤率達到 11.50%。平均響應時間爲 24 秒。

而異步 Servlet 則很是的穩定,錯誤率爲 0 ,全部的請求響應時間都控制在 500ms 到 680ms 之間,平均時間爲 541 ms,因爲 Tomcat 線程數只有 2 個,這個結果能夠接受。90% 的響應時間也是在 600 毫秒左右,屬於正常水平。

Summary

異步 Servlet 發佈已久,但彷佛 Java 社區使用的人仍是很少,不知爲什麼?但異步帶來的性能提高不言而喻。在服務器線程數較少,業務耗時的場景下,異步能明顯提升系統吞吐量,線程數以外的請求不會像同步請求同樣被拖慢。

實際上,這和 Netty 的最佳實踐是相似的,永遠不要在 IO 線程上作耗時任務,原則是:耗時任務丟進業務線程池,異步操做結束從 IO 線程返回或者直接返回。

防止 IO 線程阻塞,影響後面的請求。

相關文章
相關標籤/搜索