Play non blocking против блокировки Spring - чистая производительность Play - PullRequest
0 голосов
/ 24 декабря 2018

Я сделал несколько тестов, чтобы выяснить, действительно ли неблокирующая система ввода-вывода работает лучше, чем блокировка.

Для этого я написал сервисам: один в игре, другой - весной.Каждый сервис выполняет несколько параллельных удаленных запросов: Play использует неблокирующий WSClient, Sprint - блокирующий RestTemplate.

Но во всех тестах с различными конфигурациями (я использовал apache bench и artillery) весенняя загрузка превосходит игру.

В игре я часто получаю Connection reset by peer (54) ошибку с Apache Bench (хотя Spring прекрасно обрабатывает сохранение значений одновременных пользователей).

Это действительно смущает меня, я пытался установить различные значения вконфигурация воспроизведения (например, коэффициент параллелизма и т. д.), но, тем не менее, это не помогает.

Обе службы развернуты на aws с t2.xlarage, на котором работает ubuntu 18 (каждая служба работает на своем собственном экземпляре).1012 *

Код службы воспроизведения:

HomeController

@Singleton
class HomeController @Inject()(requestService: RequestService, cc: ControllerComponents)(implicit ec: ExecutionContext) extends AbstractController(cc) {


  def index() = Action { implicit request: Request[AnyContent] =>
    Ok(views.html.index())
  }

  val rand = new Random

  def parallelRequests() = Action.async {
    val futures = (1 to 10).map( _ =>
      requestService.makeRequest(s"http://3.17.161.135:9000?p=${rand.nextInt(99999) + 1}"),
    )

    Future.sequence(futures).map{ _ =>
      Ok("Done")
    }
  }
}

Запрос на обслуживание

@Singleton
class RequestService @Inject()(wsClient: WSClient)(implicit ec: ExecutionContext){

  def makeRequest(url: String): Future[String] = {
      wsClient.url(url).get().map { r =>
        r.body
      }
  }

}

application.conf

# https://www.playframework.com/documentation/latest/Configuration
play.filters.enabled += play.filters.hosts.AllowedHostsFilter

play.filters.hosts {
  # Allow requests to example.com, its subdomains, and localhost:9000.
  allowed = ["."]
}

play.application.secret = "supersecret"

akka {
  http {
    server {
      max-connections = 1024
    }
  }
  actor {
    default-dispatcher {
      # This will be used if you have set "executor = "fork-join-executor""
      fork-join-executor {
        # Min number of threads to cap factor-based parallelism number to
        parallelism-min = 8

        # The parallelism factor is used to determine thread pool size using the
        # following formula: ceil(available processors * factor). Resulting size
        # is then bounded by the parallelism-min and parallelism-max values.
        parallelism-factor = 3.0

        # Max number of threads to cap factor-based parallelism number to
        parallelism-max = 64

        # Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack
        # like peeking mode which "pop".
        task-peeking-mode = "FIFO"
      }
    }
  }
}

(здесь я пробовал разные вещи, включая конфигурацию по умолчанию для игры)

Полный исходный код службы воспроизведения:https://github.com/teimuraz/benchmark-play-async

Пружинный сервис

Контроллер

@RestController
public class Controller {

    private RequestService requestService;

    @Autowired
    public Controller(RequestService requestService) {
        this.requestService = requestService;
    }

    Random rand = new Random();

    @GetMapping
    public String home() {
        return "Hi";
    }

    @GetMapping("/parallel-requests")
    public String parallelRequests() throws InterruptedException {
        CompletableFuture<String> res1 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res2 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res4 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res5 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res6 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res7 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
//        CompletableFuture<String> res8 = requestService.makeRequest("https://amazon.com?p=" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res9 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
        CompletableFuture<String> res10 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);

        CompletableFuture.allOf(res1, res2, res4, res5, res6, res7,  res9, res10).join();
        return "Done";
    }
}

RequestService

@Service
public class RequestService {

    private static final Logger logger = LoggerFactory.getLogger(RequestService.class);

    private final RestTemplate restTemplate;

    @Autowired
    public RequestService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Async
    public CompletableFuture<String> makeRequest(String url) throws InterruptedException {
//        logger.info("Making request to url " + url);
        String result = "";
        result = restTemplate.getForObject(url, String.class);
        return CompletableFuture.completedFuture(result);
    }
}

Приложение

@SpringBootApplication
@EnableAsync
public class BenchmarkApplication {

    public static void main(String[] args) {
        SpringApplication.run(BenchmarkApplication.class, args);
    }

    @Bean
    public Executor taskExecutor() {
        return Executors.newFixedThreadPool(400);

    }
}

application.properties

server.tomcat.max-threads=500

Полный исходный код https://github.com/teimuraz/becnhmark-spring-blocking

Результаты тестов с артиллерией

artillery quick --count 500 -n 20 [url]

Play

Summary report @ 14:47:25(+0400) 2018-12-24
  Scenarios launched:  500
  Scenarios completed: 411
  Requests completed:  8614
  RPS sent: 104.45
  Request latency:
    min: 150.9
    max: 72097.2
    median: 195
    p95: 1793.2
    p99: 12623.9
  Scenario counts:
    0: 500 (100%)
  Codes:
    200: 8614
  Errors:
    ECONNRESET: 89

Spring

Summary report @ 14:49:14(+0400) 2018-12-24
  Scenarios launched:  500
  Scenarios completed: 500
  Requests completed:  10000
  RPS sent: 600.24
  Request latency:
    min: 155.2
    max: 3550.4
    median: 258.9
    p95: 500.8
    p99: 1370.7
  Scenario counts:
    0: 500 (100%)
  Codes:
    200: 10000    

150 RPS в игре против 600 весной !!!

Результаты теста с ab

ab -n 5000 -c 200 -s 120 [url]

Воспроизведение

apr_socket_recv: Connection reset by peer (54)

Spring

Concurrency Level:      200
Time taken for tests:   23.523 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      680000 bytes
HTML transferred:       20000 bytes
Requests per second:    212.56 [#/sec] (mean)
Time per request:       940.924 [ms] (mean)
Time per request:       4.705 [ms] (mean, across all concurrent requests)
Transfer rate:          28.23 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      153  352 198.5    303    1485
Processing:   163  364 242.4    319    6589
Waiting:      163  363 240.3    318    6589
Total:        324  716 313.2    642    7003

Что я делаю не так с игрой?

Какие значения в конфигурации я должен настроить, чтобы игра стала лучше?

Или неблокирование это просто ажиотаж и модное слово?

...