簡體   English   中英

Spring同步與異步REST控制器

[英]Spring sync vs async rest controller

我嘗試查看Spring同步REST控制器與同一控制器的異步版本之間的區別。

每個控制器都做同樣的事情:獲取一個RequestBody並將其保存在Mongo數據庫中。

@RestController
@RequestMapping ("/api/1/ticks")
public class TickController {

    @Autowired
    private TickManager tickManager;

    @RequestMapping (method = RequestMethod.POST)
    public ResponseEntity save(@RequestBody List<Tick> ticks) {
        tickManager.save(ticks);

        return new ResponseEntity(HttpStatus.OK);
    }

    @RequestMapping (value = "/async", method = RequestMethod.POST)
    public @ResponseBody Callable<ResponseEntity> saveAsync(@RequestBody List<Tick> ticks) {
        return () -> {
            tickManager.save(ticks);

            return new ResponseEntity(HttpStatus.OK);
        };
    }
}

tickManager僅對tickRepository具有依賴性,只需要調用子層即可。

tickRepository基於Spring Data Mongodb:

@Repository
public interface TickRepository extends MongoRepository<Tick, String> {}

我使用加特林(Gatling)測試那些控制器。 這是我的情況:

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TicksSaveSyncSimulation extends Simulation {

  val rampUpTimeSecs = 20
  val testTimeSecs   = 5
  val noOfUsers      = 1000
  val minWaitMs      = 1000 milliseconds
  val maxWaitMs      = 3000 milliseconds

  val baseURL      = "http://localhost:9080"
  val requestName  = "ticks-save-sync-request"
  val scenarioName = "ticks-save-sync-scenario"
  val URI          = "/api/1/ticks"

  val httpConf = http.baseURL(baseURL)

  val http_headers = Map(
    "Accept-Encoding" -> "gzip,deflate",
    "Content-Type" -> "application/json;charset=UTF-8",
    "Keep-Alive" -> "115"
  )

  val scn = scenario(scenarioName)
    .repeat(100) {  
      exec(
        http(requestName)
          .post(URI)
          .headers(http_headers)
          .body(StringBody(
            """[{
              |  "type": "temperature",
              |  "datas": {}
              |}]""".stripMargin))
          .check(status.is(200))
      )
    }

  setUp(scn.inject(rampUsers(1000) over (1 seconds))).protocols(httpConf)
}

我嘗試了幾種情況,同步版本每秒處理的請求總是比異步版本多2倍。 當我增加用戶數量時,這兩個版本會崩潰。

我試圖覆蓋異步版本的taskExecutor,但沒有成功:

@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(1000);
        taskExecutor.setThreadNamePrefix("LULExecutor-");
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

我認為看到異步實現有所不同。 我究竟做錯了什么?

您的測試似乎有缺陷。 在管道的一端(這里是您的控制器)不阻塞,而在另一端阻塞( tickManager.save確實看起來像是阻塞調用),這沒有任何意義。 您只需支付進入ThreadPoolTaskExecutor的額外費用。

然后,通常來說,當您的所有任務都非常快時(例如勾號),您將不會從非阻塞架構中獲得任何收益。 當某些任務花費更長的時間時,您可以期望獲得收益,因此,您不想浪費資源(線程,CPU周期)只是等待它們完成,而您想同時使用它們來執行其他任務。

關於Too many open files異常,您可能尚未正確調整操作系統以進行負載測試,請查看相關文檔 您還很有可能在同一主機上運行您的應用程序和Gatling(可能還有您的數據庫),這很不好,因為它們會爭奪資源。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM