Комментарии 23
Java может быть быстрой, чтобы в этом убедиться, нужно смотреть Web Framework Benchmarks, там каждый год тестируют фреймворки на разных языках, несколько сотен. Если кратко, могут C/C++, может Rust. Go пока не может. Java тоже может, хотя и не так хорошо, как C/C++ Rust. Spring не может и никогда не мог.
Результаты Web Framework Benchmarks, кстати, лучше перепроверять. А то как минимум год назад был казус, когда разработчики ASP.NET core нарушили правила бенчмарка в своём предложенном решении(разработки фреймворков имеют право сабмитить свои реализации бенчмарка, чтобы они, зная как всё работает внутри, продемонстрировали максимальную производительность), и, например, генерировали ответ на запрос не шаблонизатором, а сложением строк, и также вручную дёргали лежащий в основе фреймворка веб-сервер, вместо того, чтобы дёргать его через фреймворк, как предписывали правила.
А разве не Java там в списках лидеров, обгоняя C++? Ясно, что не по памяти, но всё же.
Интересно, что же все таки случится в «правильном тесте» webflux, если добавить к вызову репозитория switch context скажем bounded elastic пул. В MVC - где тесты с тем же CompletableFuture, а как же loom вместо стандартной фабрики потока на запрос?
Описание пайплайна не верное, тест можно считать провальным.
Не соглашусь. Идея теста была в том, чтобы сравнить фреймворки "из коробки" без глубокого тюнинга и оптимизаций. Понятно, что если в каждом решении поиграть с настройками, то можно получить небольшой прирост производительности. Частично я это показал, заменив Data Jdbc на более легковесную библиотеку в одном из тестов.
По тезисам:
если добавить к вызову репозитория switch context скажем bounded elastic пул
Тестирование различных пулов Reactor стоит оставить для статей по реактору.
а как же loom вместо стандартной фабрики потока на запрос
Аналогично, такие сравнение следует оставить специализированным статьям. Например, этой: Project Loom и Spring Boot: тесты производительности. Спойлер - не поможет, т.к. Tomcat не был заточен под Project Loom.
В MVC - где тесты с тем же CompletableFuture
Чем в данном случае поможет CompletableFuture? Если завернуть вызов еще в один поток выполнения, производительность от этого не вырастет.
Тест с reactor в корне неверный. Обратитесь к документации, где четко сказано, что любое IO необходимо публиковать на выделенный Schedulers. Иначе, что вы хотите сказать графиками - reactor хуже servlet stack?
Тестирование различных пулов Reactor стоит оставить для статей по реактору.
Если тестировать - то правильно. Как минимум по графикам уже понятно, чем event loop занят 99% времени.
Так это херня, а не тесты - сравнение круглого с квадратным. Вы что тестируете Java или кучу кода с непонятными функциями, которые под капотом фреймворков? Вроде в банкинге и финтехе никто не жалуется на медленность Java.
на каждый HTTP запрос создается поток, который блокируется при любом блокирующем вводе-выводе
Такую хрень разве что студенты-самоучки по доисторическим учебникам могут написать. Java 1.5 зарелизилась 19 лет, в ней появились пулы потоков "из коробки". И создавать/пускать потоки вручную с той поры следует лишь в случае, когда ты реально понимаешь зачем тебе это надо.
Среди блокирующих серверов наиболее часто используются embedded-сервера Tomcat, Jetty.
Интересно с какого это перепоя tomcat и jetty вдруг опять стали блокирующими? Синхронная обработка запросов != блокирующий сервер. А jetty, емнип, вообще изначально писался на неблокирующем nio.
Речь шла как раз о блокируещем IO. Который по умолчанию используется в Tomcat и Jetty. В статье нет цели разобрать работу указанных веб-серверов в деталях, лишь схематически обозначить разницу в подходах.
создавать/пускать потоки вручную с той поры следует лишь в случае, когда ты реально понимаешь зачем тебе это надо.
Никто и не говорит, о том что при блокирующем IO не используются пулы потока. Но это не изменяет того факта, что на каждый запрос будет выделен поток из пула, который освободится только после завершения обработки этого запроса.
Такую хрень разве что студенты-самоучки по доисторическим учебникам могут написать
Если не умеете общаться уважительно и по делу, то, возможно, вообще не стоит писать комментарии.
Никто и не говорит, о том что при блокирующем IO не используются пулы потока.
А как это понимать?
на каждый HTTP запрос создается поток
Не выделяется, не назначается, а именно создаётся.
не умеете общаться уважительно
Ах, прошу меня великодушно извинить за то, что ранил Вашу тонкую натуру.
по делу
Я, собственно, по делу и написал: создавать (прошу заметить, не получать из пула, а именно создавать) отдельный поток на запрос - это самый распространённый подход при реализации сетевого общения в примерах учебников года до 2007 уж точно. И на сегодняшний день это является антипаттерном и не рекомендуется к использованию.
Пo антипаттерну: ну вот с появлением виртуальных тредов это больше не антиаттерн. Нужен поток - создай виртуальный, сделай в нем что нужно, и выброси. Это отныне очень дешево и производительно. Времена меняются!
Думаю, что Вы видите разницу между "создай поток" и "создай виртуальный поток".
И даже в этом случае, дабы было единообразие в коде, лучше использовать специальный ExecutorService.
В целом да, можно придраться. Но, как человек, который работает с виртуальными тредами каждый день, могу сказать, что фраза "создать поток" для меня уже давно значит создать именно виртуальный поток. А создать несущий тред (carrier thread) или реальный тред - говорит о том реальном треде.
Не понимаю за что заминусили автора
За то, что он рассуждает о том, в чём мало понимает. И за то, что архитектор решения написал статью достойную джуна.
Статья позиционируется как рассуждение о производительности серверов, основанных на разных подходах, и, вероятно, автор допустил ошибки в рассуждениях о реактивщине. Другое дело, что кроме пары сектантов её никто и не понимает - у людей достаточно проблем с бизнес логикой, а тут у тебя ещё какие-то кросс-функциональные вещи повсюду в коде
Честно говоря ... бестолковая статья т.к. сравниваются .... web сервер с фреймворком высокого уровня. Судя по представленному выше коду, логичнее сравнивать Nima с Netty, Undertow и подобными вещами.
Спасибо за статью. Главный момент тут в том, что правильное использование Virtual Threads, дает производительность, сравнимую с Netty, но без рективщины. Блокинг код более понятный, его проще отлаживакть и проще писать.
Мы начали делать Ниму более года назад в тесной кооперации с ребятами их JDK. Было важно именно написать веб-сервер с нуля, а не переделывавть существующий. Мы пробовали, но решение было половинчатым и не давало преимуществ. А Нима дает. Ребята из Java испоьзуют Нима для тестирования и на презентациях, как пример удачного использования Virtual Threads. И новый Хелидон, в основе которого лужит Нима, тоже очень хорош по производительности. Кому интересно, RC1 уже доступен, GA планируется в этом месяце.
По поводу бенчмарков, если интересны другие тесты, то независимые от TechEmpower можно посмореть по ссылке. Там и Quarkus есть и Spring Boot.
Во-первых хочу сказать - большое спасибо за статью! Она отличная!
Во-вторых.. Прочитав все коментарии, мне показалась, что все описанное слишком “революционно” для многих комментаторов. И мне тоже показалось, что большинство все еще не делают разницы между “обыкновенными” тредами и виртуальными тредами.
Я попытаюсь внести немного больше ясности и в терминологию Хелидона, хотя автор в целом все правильно написал.
Helidon Nima это внутреннее название веб сервера написанного с нуля в блокирующей парадигме с использованием виртуальных тредов. Ныне официально этот веб сервер называется Helidon Web Server (вот так просто). Старое имя Nima больше не используется.
Helidon SE и Helidon MP это уже полноценные фреймворки для написания микросервисов. SE использует максимально “джавовый” стиль программирования, а MP сертифицирована по MicroProfile спецификации, и в ней много всякой магии в стиле спрингов (типа поставил пару аннотаций и все само заработало).
В версиях 1-3 низкоуровневым веб сервером служил Netty. В Helidon SE был свой отдельный независимый реактивный движок Helidon Reactive Engine (его, кстати, помогал писать сам Дейвид Карнок). Все API фреймворка Helidon SE были реактивными на базе этого движка и Netty в основе. То есть, если хочешь написать микросервис, то надо написать его в реактивной парадигме используя Single, Multi (хелидоновские аналоги Mono и Flux) и кучей реактивных операторов.
С появлением Helidon Web Server (в прошлом Helidon Nima) было принято решение переписать весь Helidon SE под “блокирующую” парадигму и полностью убрать Netty. Тем самым фреймворк Helidon SE 4 обзавелся новым “нереактивным блокирующим” API и новым Helidon Web Server. То есть с версии 4 нет всех этих Single и Multi и реактивных операторов. Просто пишется блокирующий код, как нас учили в 5м классе.
И что получилось - команде Helidon мы сравнивали Netty с Helidon Web Server, и этот Helidon Web Server оказался на пару процентов быстрее. Здророво то, что код самого веб сервера стал меньше по объему, понятнее и легким в отладке.
Насколько я вижу, автор сравнивает webflux именно с Helidon SE 4 (который работает на основе Helidon Web Server). То есть сравнивает фреймворки. И насколько я вижу из примера, там не только сам Helidon Web Server, но и метрики и всякие observability фичи, и т.д.
Поэтому сравнение вполне корректно.
Я погонял пример у себя (M1 Max, 64 Gb RAM, Aarch64 JDK21), и результаты схожие.
Так что, все вышеописанное верно! Еще раз спасибо за статью!
Ознакомиться с результатами бенчмарков можно из ответа m0mus.
Продвижение Project Loom, это здорово. Будут интересны результаты Spring MVC, когда появится "коробочная" версия, без танцев с бубном.
В тестах Spring MVC, надеюсь, количество tomcat потоков было увеличено с дефолтных 100?
Советую сделать тесты с jetty - изменение одной строки, по прежнему коробочный MVC, а производительность намного выше (по моим наблюдениям).
Хочется отметить ещё раз важность понимания что именно тестируется. Тесты WebMvc Jdbi это наглядно демонстрируют. Может получиться так, что не так далеко нужно уйти от коробочной версии, чтобы получит схожий буст производительности. Ещё пример - Spring Data и нативный R2DBC имеют разницу в примерно 1.5 раза.
Все таки важно тестировать на разных машинах. В частности, сетевые запросы к бд, вроде бы, и не идут через local loopback, из-за использования докера, но подозреваю, что все таки, это намного быстрее реального сетевого запроса. В реальности вся видимая разница может исчезнуть.
Важно тестировать на нескольких CPU. Например, R2DBC прям до недавнего времени имел огромную деградацию, проявляющуюся на многоядерности.
Про деньги - как бы да, дешевле. Но нужно помнить и про затраты на танцы с новым фреймворком. Его дружбе со всей другой экосистемой итп. Посчитайте сколько стоит один разраб в год, занимающийся этим.
Как по мне,
WebServer.builder()
такая же "магия" как и@RestController
. Как ни крути, любой фреймворк, его поведения итд нужно учить и понимать. Не избежать этого, хоть с аннотациями, что без.Согласен, Java быстрая. Нужны просто прямые руки. А если прям 200% уверены, что ботлнек не в бд, то есть Vertx с Vertx Sql Client.
Помните, переписав свой проект с нуля, зачастую получится быстрее даже на старом фреймворке! :)
Может ли Java быть быстрой? Сравнение производительности Helidon Níma и Spring