107 lines
10 KiB
Markdown
107 lines
10 KiB
Markdown
---
|
||
aliases:
|
||
tags:
|
||
- maturity/🌱
|
||
- type/source/lecture
|
||
date: 2024-10-02
|
||
zero-link:
|
||
parents:
|
||
linked:
|
||
---
|
||
**Организатор**:: [[../../meta/organizations/Т-Банк|Т-Банк]]
|
||
**Конференция**:: [[../conference/Конференция. JVM Day 2024|Конференция. JVM Day 2024]]
|
||
**Автор**:: [[../../meta/people/Сергей Петрелевич|Сергей Петрелевич]]
|
||
**Ссылка::** [Могут ли Virtual threads заменить Webflux? — Сергей Петрелевич, Squad - YouTube](https://www.youtube.com/watch?v=SOvzg-uoVco)
|
||
|
||
***
|
||
![Могут ли Virtual threads заменить Webflux? — Сергей Петрелевич, Squad - YouTube](https://www.youtube.com/watch?v=SOvzg-uoVco)
|
||
***
|
||
**О чем доклад:** Автор пытается понять есть ли место реактивной парадигме в современной Java, после выхода [[../../dev/java/Java Virtual Threads|виртуальных потоков]]. Для этого он проводит нагрузочное тестирование и сравнивает результаты.
|
||
|
||
## Тезисы
|
||
- Прикладным разработчикам удобнее работать с абстракциями высшего порядка (Spring WebFlux, Quarkus Reactive).
|
||
- Реактивный подход остается актуальным в некоторых задачах. Архитектору как и прежде необходимо выбирать подходящий инструмент для конкретной задачи.
|
||
- Виртуальные потоки могут дать существенный прирост (в 2 раза) производительности существующему приложению.
|
||
- Виртуальные потоки легко включить, от разработчика не требуется переписывать существующий код.
|
||
- Реактивный подход (Spring WebFlux) позволит дать максимальный прирост производительности (в 3.8 раза), но за это придется заплатить.
|
||
- [[../../dev/architecture/Реактивное программирование|Реактивное программирование]] требует от разработчика изучение новой парадигмы и новых подходов: "функциональный" стиль разработки (Fluent API).
|
||
***
|
||
## Конспект
|
||
Автор считает, что Java не самый эффективный язык программирования с точки зрения потребления ресурсов
|
||
|
||
Имеется куча вариантов оптимизации, которые автор предлагает рассмотреть. Рассматривать начинаем с двух концепций
|
||
- [[../../dev/architecture/Один клиент — один поток|Один клиент — один поток]]
|
||
- С таким подходов в Java быстро доходим до пределов количества потоков, которые возможно создать
|
||
- Применим, если нагрузка на систему не большая
|
||
- [[../../dev/architecture/Много клиентов — один поток|Много клиентов — один поток]]
|
||
- Кажется, что это то что надо, но это не всегда так
|
||
|
||
Автор рассматривает [[../../dev/architecture/Event Loop|Event Loop]]. Реализация в Java: NIO Selector.
|
||
|
||
Можно работать напрямую с NIO, но прикладным разработчикам удобнее работать с Netty, который позволяет работать с более высокоуровневыми абстракциям, при этом сохраняя все преимущества NIO. Но есть еще более высокоуровневая абстракция поверх Netty.
|
||
|
||
NIO посылает события Netty их преобразовывает в более удобный вид. Далее можно сделать [[../../../../_inbox/Callback|Callback-и]], на основе этого работает Vert.x. Второй подход это использовать реактивный API: Reactor Netty. Именно этот подход автор и будет рассматривать.
|
||
|
||
![[../../meta/files/images/Pasted image 20241003080932.png]]
|
||
|
||
Можно реализовывать свои приложения сразу на Reactor Netty, но еще удобнее использовать Spring Webflux. Это реактивный функциональный (позволяет разрабатывать в функциональном стиле) http-сервер, который эффективно использует ресурсы.
|
||
|
||
Пример "функционального" подхода. Fluent API
|
||
![[../../meta/files/images/Pasted image 20241003081345.png]]
|
||
|
||
Теперь рассмотрим [[../../dev/java/Java Virtual Threads|виртуальные потоки]], которые появились в Java 21. Один платформенный поток Java может работать со множеством клиентов одновременно. По сути виртуальный поток это объект класса, который выполняется на базе платформенного потока, который в свою очередь является фактически [[../../dev/fundamental/Поток процесса ОС|потоком ОС]].
|
||
|
||
![[../../../../garden/ru/meta/files/images/Pasted image 20241003081726.png]]
|
||
|
||
Виртуальный поток помогает с операциями, где есть [[../../dev/architecture/Блокирующий вызов|блокирующие операции]], где нужно "подождать". В таком случае ожидание практически ничего не стоит, так как виртуальный поток на это время блокируется и уступает платформенный поток другому виртуальному потоку. Но если вам нужно что-то считать, если у вас какие-то тяжелые CPU задачи, то виртуальные потоки вам не подходят.
|
||
|
||
Для работы с виртуальными потоками в SpingBoot достаточно включить одну проперти.
|
||
|
||
Примеры: [jvm-digging/virtual-thread at master · petrelevich/jvm-digging · GitHub](https://github.com/petrelevich/jvm-digging/tree/master/virtual-thread)
|
||
|
||
Итого, Sping WebFlux позволял нам обрабатывать множество запросов на одном потоке. И виртуальные потоки нам позволяют делать по сути то же самое. При этом не нужно разбираться в реактивной парадигме разработки. Тогда зачем нам WebFlux?
|
||
|
||
## Сравнение производительности
|
||
Для этого сравним два подхода на примере [небольшого приложения](https://github.com/petrelevich/jvm-digging/tree/master/virtual-thread), которое будет принимать [[../../dev/system-design/RESTful|REST]] запросы и отправлять их в кафку, после чего отвечать клиенту что сообщение доставлено.
|
||
|
||
![[../../meta/files/images/Pasted image 20241003083724.png]]
|
||
|
||
![[../../meta/files/images/Pasted image 20241003083758.png]]
|
||
|
||
Сравнение производительности. Генератор нагрузки: https://github.com/rakyll/hey
|
||
|
||
```
|
||
/hey -n=1000000 -C=300
|
||
```
|
||
- 300 клиентов отправляют 1000000 запросов
|
||
- Ethernet 1000 Mb/s
|
||
- Запускаем в докере: 256 Mb, 1 cpu.
|
||
- [[../../dev/java/gc/Garbage Collector|Garbage Collector]]: G1
|
||
- Прогрев: два запуска
|
||
- Измерений: 7
|
||
- Стандартное отклонение: 3,8; 3,7; 2,6
|
||
|
||
![[../../meta/files/images/Pasted image 20241003084259.png|500]]
|
||
|
||
Прирост производительности: 200% Virtual Thread по сравнению с платформенными потоками, а если переключиться на Webflux, то мы получим **поверх** еще 30%.
|
||
|
||
Переход на Virtual Thread максимально прост, при этом максимально эффективен. Поэтому особого смысла переписывать старые приложения на рекативной подход нет, проще включить виртуальные потоки.
|
||
### Причины
|
||
Почему Webflux производительней виртуальных потоков?
|
||
- Может быть, где-то в коде есть synchronized? События jdk.VirtualThreadPinned не фиксируются
|
||
- Замедление, наиболее вероятно, связано с переключением на платформенные потоки. JMC показывает множество событий: jdk.VirtualThreadStart, jdk.VirtualThreadEnd. При этом их длительность 0 (WTF?).
|
||
## Дополнительные материалы
|
||
-
|
||
***
|
||
## Мета информация
|
||
**Область**:: [[../00 Источники|00 Источники]]
|
||
**Родитель**::
|
||
**Источник**::
|
||
**Создана**:: [[2024-10-02]]
|
||
**Автор**::
|
||
### Дополнительные материалы
|
||
-
|
||
|
||
### Дочерние заметки
|
||
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->
|