Статьи

Необработанные показатели производительности — Spring Boot 2 Webflux против Spring Boot 1

Моя установка для теста производительности следующая:

Примеры приложений предоставляют конечную точку (/ passthrough / message), которая, в свою очередь, вызывает нисходящую службу. Сообщение запроса к конечной точке выглядит примерно так:

1
2
3
4
5
{
  "id": "1",
  "payload": "sample payload",
  "delay": 3000
}

Нисходящая служба будет задерживать на основе атрибута «delay» в сообщении (в миллисекундах).

Приложение Spring Boot 1

Я использовал Spring Boot 1.5.8.RELEASE для версии приложения Boot 1. Конечной точкой является простой контроллер Spring MVC, который, в свою очередь, использует RestTemplate Spring для выполнения нисходящего вызова. Все происходит синхронно и блокируется, и я использовал встроенный контейнер Tomcat по умолчанию в качестве среды выполнения. Это необработанный код для последующего вызова:

1
2
3
4
5
public MessageAck handlePassthrough(Message message) {
    ResponseEntity<MessageAck> responseEntity = this.restTemplate.postForEntity(targetHost
                                                            + "/messages", message, MessageAck.class);
    return responseEntity.getBody();
}

Приложение Spring Boot 2

Версия приложения Spring Boot 2 предоставляет конечную точку на основе Spring Webflux и использует WebClient , новую неблокирующую реактивную альтернативу RestTemplate для выполнения нисходящего вызова — я также использовал Kotlin для реализации, которая не влияет на производительность. Сервер выполнения Netty :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.BodyInserters.fromObject
import org.springframework.web.reactive.function.client.ClientResponse
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.bodyToMono
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyToMono
import reactor.core.publisher.Mono
 
class PassThroughHandler(private val webClient: WebClient) {
 
    fun handle(serverRequest: ServerRequest): Mono<ServerResponse> {
        val messageMono = serverRequest.bodyToMono<Message>()
 
        return messageMono.flatMap { message ->
            passThrough(message)
                    .flatMap { messageAck ->
                        ServerResponse.ok().body(fromObject(messageAck))
                    }
        }
    }
 
    fun passThrough(message: Message): Mono<MessageAck> {
        return webClient.post()
                .uri("/messages")
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .body(fromObject<Message>(message))
                .exchange()
                .flatMap { response: ClientResponse ->
                    response.bodyToMono<MessageAck>()
                }
    }
}

Детали теста производительности

Тест прост, для разных наборов одновременных пользователей (300, 1000, 1500, 3000, 5000) я отправляю сообщение с атрибутом задержки, установленным на 300 мс, каждый пользователь повторяет сценарий 30 раз с задержкой от 1 до 2 секунд между запросами. Я использую отличный инструмент Гатлинга для генерации этой нагрузки.

Полученные результаты

Вот результаты, полученные Гатлингом:

300 одновременных пользователей:

Ботинок 1 Boot 2

1000 одновременных пользователей:

Ботинок 1 Boot 2

1500 одновременных пользователей:

Ботинок 1 Boot 2

3000 одновременных пользователей:

Ботинок 1 Boot 2

5000 одновременных пользователей:

Ботинок 1 Boot 2

Как и ожидалось, когда число одновременно работающих пользователей остается низким (скажем, менее 1000), Spring Boot 1 и Spring Boot 2 хорошо справляются с нагрузкой, а время отклика в 95 процентилей остается на миллисекунды выше ожидаемой задержки в 300 мс.

При более высоких уровнях параллелизма асинхронный неблокирующий ввод-вывод и реактивная поддержка в Spring Boot 2 начинают показывать свои цвета — время 95-го процентиля даже при очень большой нагрузке в 5000 пользователей остается на уровне около 312 мс! Spring Boot 1 регистрирует множество сбоев и большое время отклика на этих уровнях параллелизма.

У меня есть все примеры и скрипты загрузки, доступные в моем репозитории github — https://github.com/bijukunjummen/boot2-load-demo.

См. Оригинальную статью здесь: Необработанные показатели производительности — Spring Boot 2 Webflux против Spring Boot 1

Мнения, высказанные участниками Java Code Geeks, являются их собственными.