Привет, я Миц. Это первый пост на dev.to . Приятно познакомиться:)
Как многие из вас слышали, Микросервисы, блокчейн и т.д., Существует множество технических модных словечек, и они часто приходят и уходят. Поэтому для нас важно не верить им, даже не проверив, а самим увидеть, как они работают, а затем поместить их в наш набор инструментов. Вот почему на этот раз я попробовал использовать часть Реактивного программирования.
Одним из преимуществ реактивного программирования является то, что мы можем эффективно использовать машинные ресурсы. Например, в случае веб-приложения сервер может обрабатывать больше запросов, чем приложение в стиле блокировки с меньшим количеством потоков.
SpringBoot2, который был выпущен в начале этого месяца, представил функцию “Реактивный веб”. Поэтому я попытался сравнить “Spring Web” и “Spring Reactive Web”. ” Spring Web” основан на обычном стиле блокировки с помощью сервлета, а “Spring Reactive Web” – это новый стиль с реактивным программированием.
(Изображение с сайта (Изображение с сайта
Я нашел отличную статью, в которой сравниваются Spring Boot 1 и SpringBoot2: Необработанные показатели производительности – Spring Boot 2 Webflux vs. Пружинный ботинок 1 . Я сделал почти то же самое, чтобы сравнить SpringBoot2, блокирующий Веб, и Реактивный Веб. Архитектура выглядит следующим образом:
Есть 3 приложения:
- задержка-сервис
- блокировка-приложение
- реактивный подход
Исходный код находится здесь: https://github.com/bufferings/webflux-demo-201803
задержка-сервис
Служба задержки эмулирует внешний API с некоторой задержкой. Мы можем установить задержку с помощью параметра path:
@GetMapping("/{delayMillis}")
public Mono get(@PathVariable int delayMillis) {
return Mono.just("OK")
.delayElement(Duration.ofMillis(delayMillis));
}
блокировка-приложение
Блокирующее приложение – это простое веб-приложение Spring, которое вызывает службу задержки блокирующим способом и возвращает ее блокирующим способом:
private static final String DELAY_SERVICE_URL = "http://localhost:8080";
private final RestTemplate client;
public BlockingApp(RestTemplateBuilder builder) {
client = builder.rootUri(DELAY_SERVICE_URL).build();
}
@GetMapping("/{delayMillis}")
public String get(@PathVariable String delayMillis) {
String result = client.getForObject("/" + delayMillis, String.class);
return "Blocking:" + result;
}
реактивный подход
Реактивное приложение – это реактивное веб-приложение Spring, которое вызывает службу задержки с реактивным клиентом и возвращает Mono :
private static final String DELAY_SERVICE_URL = "http://localhost:8080";
private final WebClient client = WebClient.create(DELAY_SERVICE_URL);
@GetMapping("/{delayMillis}")
public Mono get(@PathVariable String delayMillis) {
return client.get()
.uri("/" + delayMillis)
.retrieve()
.bodyToMono(String.class)
.map(s -> "Reactive:" + s);
}
Задержка запуска-сервис:
./gradlew -p apps/delay-service clean bootRun
curl -w "\n%{time_total}s\n" localhost:8080/1000
# returns "OK" after 1000ms
curl -w "\n%{time_total}s\n" localhost:8080/2000
# returns "OK" after 2000ms
Начать блокировку-приложение:
./gradlew -p apps/blocking-app clean bootRun
curl -w "\n%{time_total}s\n" localhost:8081/2000
# returns "Blocking:OK" after 2000ms
Запустить реактивное приложение:
./gradlew -p apps/reactive-app clean bootRun
curl -w "\n%{time_total}s\n" localhost:8082/2000
# returns "Reactive:OK" after 2000ms
Теперь все три приложения запущены.
Сценарий нагрузочного тестирования
Я использовал Gatling( https://gatling.io/ ) для нагрузочного теста.
Сценарий выглядит примерно так: “1000 пользователей вызывают API 30 раз с интервалом от 1 до 2 секунд”. Я хотел бы установить задержку delay-service равной 300 мс.
val myScenario = scenario("Webflux Demo").exec(
repeat(30) {
exec(
http("request_1").get(targetUrl)
).pause(1 second, 2 seconds)
}
)
setUp(myScenario.inject(rampUsers(simUsers).over(30 seconds)))
Переход от Гатлинга к блокировке-приложение
./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8081/300 \
-DSIM_USERS=1000 gatlingRun
С помощью VisualVM мы можем видеть, что количество рабочих потоков увеличивается до 200, что является значением maxthreads по умолчанию для Tomcat.
Гатлинг к реактивному подходу
./gradlew -p apps/load-test -DTARGET_URL=http://localhost:8082/300 \
-DSIM_USERS=1000 gatlingRun
Он использует только 4 потока для обработки запроса.
Вот результат.
Как вы можете видеть, для 1000 пользователей оба приложения работают нормально со временем отклика около 300 мс, как мы и ожидали. Но для 3000 и 6000 пользователей 95-процентный показатель блокировки приложений становится хуже.
С другой стороны, reactive-app сохраняет хорошую скорость отклика около 400 мс и он показывает около 2000 оборотов в минуту с моим ноутбуком (Core i7-7500U 2,7 ГГц/16 ГБ оперативной памяти).
Интересный результат!
блокировка-приложение
блокировка-приложение
блокировка-приложение
блокировка-приложение
блокировка-приложение
блокировка-приложение
блокировка-приложение
с 6000 пользователями:
с 6000 пользователями:
Поскольку я решил попробовать использовать конфигурацию по умолчанию, количество потоков Tomcat достигло 200, что является значением maxThreads по умолчанию. Вероятно, настройка максимального потока улучшит производительность блокирующего приложения.
Отделение конверта
Поскольку я пробовал эту демонстрацию на своем ноутбуке, все приложения влияли друг на друга в отношении использования ресурсов. Таким образом, разделение каждого приложения на несколько машин может привести к другому результату.
В заключение, мы могли бы узнать, как SpringBoot2 Reactive Web эффективно обрабатывает запросы. Но я рекомендую вам проверить это самостоятельно. Я думаю, что особенно реактивное программирование в веб-стиле требует, чтобы Java-инженер в какой-то степени изменил свое мышление.
В любом случае, это так интересно, и я чувствую, что теперь SpringBoot2 Reactive Web находится в моем наборе инструментов. Спасибо!
Оригинал: “https://dev.to/bufferings/springboot2-blocking-web-vs-reactive-web-46jn”