WebFlux часто вызывает путаницу, потому что его обычно объясняют со стороны инструмента, а не со стороны проблемы.
Поэтому начнем с базы.
Сначала: блокирующий vs неблокирующий код
-» Блокирующий код:
Поток встает и ждет, пока операция не завершится.
Пока ждет, он ничего больше делать не может.
-» Неблокирующий код:
Поток делегирует ожидание и остается свободным для других задач.
Когда результат готов, выполняется продолжение.
Это не быстрее.
Это просто более эффективное использование потоков.
Еще одно ключевое понятие перед тем как идти дальше:
Backpressure. Это способность системы регулировать поток данных, чтобы не перегружать потребителя.
Представь так:
потребитель может сказать продюсеру: «присылай только столько, сколько я успеваю обработать».
Теперь к WebFlux.
WebFlux позволяет строить полностью неблокирующие приложения end-to-end:
-»Неблокирующий сервер (Netty)
-»Неблокирующие API
-»Проброс backpressure без перегрузки потребителя
-»Реактивная модель (Mono, Flux)
Ключевое слово здесь end-to-end.
Если хотя бы одна часть блокирует, весь выигрыш размывается.
Самая частая ошибка при работе с WebFlux
Использовать WebFlux, а потом:
-»Дернуть блокирующую базу данных
-»Использовать SDK без реактивной поддержки
-»Делать .block(), потому что так проще
В итоге:
-»Все равно все становится блокирующим
-»Ты просто добавил сложности
-»Ничего не выиграл
WebFlux не превращает блокирующий код в неблокирующий.
А Virtual Threads разве это не меняют?
Меняют, и это важно.
С Virtual Threads:
-»Блокирующая модель снова становится жизнеспособной
-»Цена ожидания резко падает
-»Необходимость в WebFlux уменьшается
Но есть нюанс.
Virtual Threads не дают:
-»Backpressure
-»Контроль потока
-»Композицию стримов
-»Настоящий стриминг данных
Они меняют tradeoff, но не убирают саму проблему.
Когда WebFlux все еще имеет смысл
WebFlux оправдан, когда:
-»Нужен реальный backpressure
-»Ты работаешь с непрерывными стримами (SSE, WebSockets)
-»Есть fan-out из множества внешних вызовов
-»Нужно явно контролировать поток данных
По необходимости, а не потому что модно.
Когда не стоит использовать WebFlux
-»Классический CRUD
-»Сложная бизнес-логика
-»Команды без реактивного опыта
-»Проекты, где читаемость важнее конкурентности
-»Когда Virtual Threads решают задачу с меньшей когнитивной стоимостью
WebFlux это другая модель исполнения.
Если твоя проблема не в масштабной I/O-конкурентности,
WebFlux тебе не поможет.
А сегодня, с Virtual Threads, многим он уже просто не нужен.
👉 Java Portal
Поэтому начнем с базы.
Сначала: блокирующий vs неблокирующий код
-» Блокирующий код:
Поток встает и ждет, пока операция не завершится.
Пока ждет, он ничего больше делать не может.
-» Неблокирующий код:
Поток делегирует ожидание и остается свободным для других задач.
Когда результат готов, выполняется продолжение.
Это не быстрее.
Это просто более эффективное использование потоков.
Еще одно ключевое понятие перед тем как идти дальше:
Backpressure. Это способность системы регулировать поток данных, чтобы не перегружать потребителя.
Представь так:
потребитель может сказать продюсеру: «присылай только столько, сколько я успеваю обработать».
Теперь к WebFlux.
WebFlux позволяет строить полностью неблокирующие приложения end-to-end:
-»Неблокирующий сервер (Netty)
-»Неблокирующие API
-»Проброс backpressure без перегрузки потребителя
-»Реактивная модель (Mono, Flux)
Ключевое слово здесь end-to-end.
Если хотя бы одна часть блокирует, весь выигрыш размывается.
Самая частая ошибка при работе с WebFlux
Использовать WebFlux, а потом:
-»Дернуть блокирующую базу данных
-»Использовать SDK без реактивной поддержки
-»Делать .block(), потому что так проще
В итоге:
-»Все равно все становится блокирующим
-»Ты просто добавил сложности
-»Ничего не выиграл
WebFlux не превращает блокирующий код в неблокирующий.
А Virtual Threads разве это не меняют?
Меняют, и это важно.
С Virtual Threads:
-»Блокирующая модель снова становится жизнеспособной
-»Цена ожидания резко падает
-»Необходимость в WebFlux уменьшается
Но есть нюанс.
Virtual Threads не дают:
-»Backpressure
-»Контроль потока
-»Композицию стримов
-»Настоящий стриминг данных
Они меняют tradeoff, но не убирают саму проблему.
Когда WebFlux все еще имеет смысл
WebFlux оправдан, когда:
-»Нужен реальный backpressure
-»Ты работаешь с непрерывными стримами (SSE, WebSockets)
-»Есть fan-out из множества внешних вызовов
-»Нужно явно контролировать поток данных
По необходимости, а не потому что модно.
Когда не стоит использовать WebFlux
-»Классический CRUD
-»Сложная бизнес-логика
-»Команды без реактивного опыта
-»Проекты, где читаемость важнее конкурентности
-»Когда Virtual Threads решают задачу с меньшей когнитивной стоимостью
WebFlux это другая модель исполнения.
Если твоя проблема не в масштабной I/O-конкурентности,
WebFlux тебе не поможет.
А сегодня, с Virtual Threads, многим он уже просто не нужен.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍6
This media is not supported in your browser
VIEW IN TELEGRAM
Тема Islands теперь используется по умолчанию во всех IDE от JetBrains.
Более мягкий и сбалансированный внешний вид, рассчитанный на комфорт и концентрацию во время работы.
Посмотреть подробнее👉 https://jb.gg/discover-the-islands
👉 Java Portal
Более мягкий и сбалансированный внешний вид, рассчитанный на комфорт и концентрацию во время работы.
Посмотреть подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9
Реальная история:
Дизайнерское агентство разработало интерфейс приложения в сервисе Figma, но из-за того, что карты Visa и Mastercard оказались заблокированы, не смогли выгрузить проект.
Осложнялось всё тем, что они могли олатить сервис только с корпоративной карты.
Читпей организовали оплату в течение 10 минут. Ребята оплатили подписку, и выгрузили проект. Интерфейс приложения был спасён!
Что делает CheatPay:
🔘Помогает оплатить зарубежные сервисы российскими картами, в том числе Visa и Mastercard.
🔘Восстанавливают доступ к аккаунту
🔘Работают прозрачно с оплатой на ИП и всеми закрывающими документами
Например:
Потеряли доступ к Zoom, Airtable, Atlassian, Canva и другим программам для управления IT-проектами? Простаивают проекты в Adobe Photoshop, Premiere Pro и After Effects? Другие сервисы? Помогут.
Обращаться в https://t.iss.one/cheatpay_ru
Дизайнерское агентство разработало интерфейс приложения в сервисе Figma, но из-за того, что карты Visa и Mastercard оказались заблокированы, не смогли выгрузить проект.
Осложнялось всё тем, что они могли олатить сервис только с корпоративной карты.
Читпей организовали оплату в течение 10 минут. Ребята оплатили подписку, и выгрузили проект. Интерфейс приложения был спасён!
Что делает CheatPay:
🔘Помогает оплатить зарубежные сервисы российскими картами, в том числе Visa и Mastercard.
🔘Восстанавливают доступ к аккаунту
🔘Работают прозрачно с оплатой на ИП и всеми закрывающими документами
Например:
Потеряли доступ к Zoom, Airtable, Atlassian, Canva и другим программам для управления IT-проектами? Простаивают проекты в Adobe Photoshop, Premiere Pro и After Effects? Другие сервисы? Помогут.
Обращаться в https://t.iss.one/cheatpay_ru
🤣4❤1😁1
Случайные UUID убивают производительность базы данных.
Ты перешел с целочисленных ID (1, 2, 3…) на UUID (a1b2-3c4d-…) ради безопасности или распределенной генерации.
И вдруг записи в БД стали медленнее. Иногда намного.
Вот почему.
Фрагментация индексов.
Большинство индексов в БД это B-Tree (сбалансированные отсортированные деревья). Физическое расположение данных имеет значение.
1. Последовательные ID
Когда ты вставляешь последовательные числа (1, 2, 3), новые записи всегда попадают в самый правый лист индекса.
Записи предсказуемые и последовательные.
Максимальные cache hit’ы.
Страницы индекса остаются заполненными на 100%.
Это максимальная скорость, на которую способна твоя база.
2. Случайные UUIDv4
UUIDv4 равномерно случайные. Это значит, что каждая новая вставка может попасть в любое место дерева.
Из-за этого:
-» База постоянно подгружает случайные страницы с диска в память (random I/O).
-» Page split. Если целевая страница заполнена, БД вынуждена делить ее пополам, в итоге получаются две полупустые страницы.
-» Эффект швейцарского сыра. Индекс раздувается и заполняется дырками, зря тратя RAM и место на диске.
-» Когда размер индекса превышает объем доступной памяти, пропускная способность на запись может просесть на 20–90%.
3. UUIDv7
Перестань использовать UUIDv4 в качестве primary key. Используй UUIDv7 (стандартизирован в RFC 9562).
UUIDv7 содержит таймстемп в начале ID, поэтому он сортируемый.
В итоге ты получаешь лучшее из двух миров:
-» Распределенная генерация, без центрального счетчика.
-» Монотонные вставки. В B-Tree они ведут себя почти как последовательные числа, без фрагментации.
-» Безопасность. Нельзя просто угадать ID (атакующий не узнает, что пользователь 101 идет сразу после 100), хотя стоит учитывать, что время создания записи становится видно.
Итог простой: ты сохраняешь удобство UUID и избавляешься от перфоманс-потерь.
👉 Java Portal
Ты перешел с целочисленных ID (1, 2, 3…) на UUID (a1b2-3c4d-…) ради безопасности или распределенной генерации.
И вдруг записи в БД стали медленнее. Иногда намного.
Вот почему.
Фрагментация индексов.
Большинство индексов в БД это B-Tree (сбалансированные отсортированные деревья). Физическое расположение данных имеет значение.
1. Последовательные ID
Когда ты вставляешь последовательные числа (1, 2, 3), новые записи всегда попадают в самый правый лист индекса.
Записи предсказуемые и последовательные.
Максимальные cache hit’ы.
Страницы индекса остаются заполненными на 100%.
Это максимальная скорость, на которую способна твоя база.
2. Случайные UUIDv4
UUIDv4 равномерно случайные. Это значит, что каждая новая вставка может попасть в любое место дерева.
Из-за этого:
-» База постоянно подгружает случайные страницы с диска в память (random I/O).
-» Page split. Если целевая страница заполнена, БД вынуждена делить ее пополам, в итоге получаются две полупустые страницы.
-» Эффект швейцарского сыра. Индекс раздувается и заполняется дырками, зря тратя RAM и место на диске.
-» Когда размер индекса превышает объем доступной памяти, пропускная способность на запись может просесть на 20–90%.
3. UUIDv7
Перестань использовать UUIDv4 в качестве primary key. Используй UUIDv7 (стандартизирован в RFC 9562).
UUIDv7 содержит таймстемп в начале ID, поэтому он сортируемый.
В итоге ты получаешь лучшее из двух миров:
-» Распределенная генерация, без центрального счетчика.
-» Монотонные вставки. В B-Tree они ведут себя почти как последовательные числа, без фрагментации.
-» Безопасность. Нельзя просто угадать ID (атакующий не узнает, что пользователь 101 идет сразу после 100), хотя стоит учитывать, что время создания записи становится видно.
Итог простой: ты сохраняешь удобство UUID и избавляешься от перфоманс-потерь.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍5
Совет по Spring Boot: используйте TaskScheduler для реализации лёгких динамических задач. Для более сложных сценариев лучше смотреть в сторону Quartz.
Инжектим и используем:
Инжектируемый бин — это
При необходимости конфигурацию можно кастомизировать:
👉 Java Portal
Инжектим и используем:
@Autowired
TaskScheduler scheduler;
scheduler.schedule(
() -> System.out.println("Hello!"),
new CronTrigger("0 */5 * * * *") // каждые 5 минут
);
Инжектируемый бин — это
ThreadPoolTaskScheduler, встроенная реализация сразу TaskScheduler и ScheduledExecutorServiceПри необходимости конфигурацию можно кастомизировать:
@Configuration
@EnableScheduling
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("my-scheduler-");
scheduler.initialize();
return scheduler;
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В JDK 26 появилась новая фича: HttpClient, который входит в Java SE ещё со времён JDK 11, теперь поддерживает HTTP/3 😱
Подробнее
👉 Java Portal
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build(); // создать экземпляр HttpClient с HTTP/3 в качестве предпочтительной версии
URI reqURI = new URI("https://www.google.com/");
HttpRequest req = HttpRequest.newBuilder()
.uri(reqURI)
.build(); // создать экземпляр запроса
final HttpResponse.BodyHandler<String> bodyHandler =
BodyHandlers.ofString(StandardCharsets.UTF_8);
HttpResponse<String> resp = client.send(req, bodyHandler);
// отправить запрос и получить ответ в виде строки
System.out.println(
"status code: " + resp.statusCode() +
" HTTP protocol version: " + resp.version()
); // вывести код ответа и используемую версию HTTP
Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9