Я сделал несколько тестов, чтобы выяснить, действительно ли неблокирующая система ввода-вывода работает лучше, чем блокировка.
Для этого я написал сервисам: один в игре, другой - весной.Каждый сервис выполняет несколько параллельных удаленных запросов: 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
Что я делаю не так с игрой?
Какие значения в конфигурации я должен настроить, чтобы игра стала лучше?
Или неблокирование это просто ажиотаж и модное слово?