简体   繁体   English

Spring同步与异步REST控制器

[英]Spring sync vs async rest controller

I try to see a difference between Spring synchronous REST Controller vs async version of same controller. 我尝试查看Spring同步REST控制器与同一控制器的异步版本之间的区别。

Each controller do the same thing : take a RequestBody and save it in a Mongo database. 每个控制器都做同样的事情:获取一个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);
        };
    }
}

The tickManager has only a dependency on a tickRepository and just do call to sub-layer. tickManager仅对tickRepository具有依赖性,只需要调用子层即可。

The tickRepository is based on Spring Data Mongodb: tickRepository基于Spring Data Mongodb:

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

I use Gatling to test those controllers. 我使用加特林(Gatling)测试那些控制器。 This is my scenario: 这是我的情况:

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

I tried several situations and the sync version always handle 2 times more request by second than the async version. 我尝试了几种情况,同步版本每秒处理的请求总是比异步版本多2倍。 When I increase the number of users the two versions crash. 当我增加用户数量时,这两个版本会崩溃。

I tried to override the taskExecutor for the async version with no more success: 我试图覆盖异步版本的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();
    }
}

I thought see a difference in favor of the async implementation. 我认为看到异步实现有所不同。 What am I doing wrong? 我究竟做错了什么?

Your test looks to be flawed. 您的测试似乎有缺陷。 It doesn't make any sense being non blocking at one end of the pipeline (here, your controllers), and being blocking at the other end ( tickManager.save really looks like a blocking call). 在管道的一端(这里是您的控制器)不阻塞,而在另一端阻塞( tickManager.save确实看起来像是阻塞调用),这没有任何意义。 You're just paying the extra cost of jumping into a ThreadPoolTaskExecutor . 您只需支付进入ThreadPoolTaskExecutor的额外费用。

Then, generally speaking, you won't gain anything from a non blocking architecture when all your tasks are very fast, like a tick. 然后,通常来说,当您的所有任务都非常快时(例如勾号),您将不会从非阻塞架构中获得任何收益。 You can expect gains when some tasks take some longer time, so you don't want to waste resources (threads, CPU cycles) just waiting for those to complete, and you want to use them to perform other tasks in the meanwhile. 当某些任务花费更长的时间时,您可以期望获得收益,因此,您不想浪费资源(线程,CPU周期)只是等待它们完成,而您想同时使用它们来执行其他任务。

Regarding your Too many open files exception, you probably haven't properly tuned your OS for load testing, check relevant documentation . 关于Too many open files异常,您可能尚未正确调整操作系统以进行负载测试,请查看相关文档 There's also a good chance that you're running your app and Gatling (and possibly your database too) on the same host, which is bad as they'll compete for resources. 您还很有可能在同一主机上运行您的应用程序和Gatling(可能还有您的数据库),这很不好,因为它们会争夺资源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM