Spring同步与异步休息控制器

时间:2016-01-22 15:18:27

标签: java spring asynchronous gatling

我试图看到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();
    }
}

我认为看到有利于异步实现的差异。 我做错了什么?

1 个答案:

答案 0 :(得分:1)

您的测试看起来有缺陷。在管道的一端(这里是你的控制器)没有阻塞是没有任何意义的,并且在另一端阻塞(tickManager.save看起来像阻塞调用)。您只是支付了跳入ThreadPoolTaskExecutor的额外费用。

然后,一般来说,当你的所有任务都非常快时,你就不会从非阻塞架构中获得任何东西。当某些任务需要较长时间时,您可以获得收益,因此您不希望浪费资源(线程,CPU周期)等待完成这些任务,并且您希望在此期间使用它们来执行其他任务。

关于您的Too many open files例外,您可能没有正确调整操作系统以进行负载测试,请检查relevant documentation。您也很有可能在同一台主机上运行您的应用程序和Gatling(以及可能还有您的数据库),这很糟糕,因为它们会竞争资源。