Рубрики
Без рубрики

В Project Loom Реактивная модель и сопрограммы

Java 15 увидит первый выпуск Project Loom. Я считаю, что это изменит правила игры для JVM… С тегами project loom, сопрограммы, реактивное программирование, java.

Java 15 увидит первый выпуск Project Loom . Я считаю, что это изменит правила игры для JVM. В этом посте я хотел бы немного углубиться в причины, которые заставляют меня поверить в это.

Во-первых, нам нужно понять основную проблему. Затем я попытаюсь описать, как предыдущие технологии пытаются решить эту проблему. После этого мы увидим подход, применяемый Project Look. Наконец, я экстраполирую то, какое влияние последнее может оказать на экосистему.

Нарезание резьбы

Сначала мы должны помнить, что долгое время компьютеры имели одно ядро. Даже тогда, когда запуск нескольких программ одновременно был обязательным требованием: речь шла о запуске по крайней мере двух, операционной системы и собственно программы.

Чтобы создать иллюзию параллелизма, он полагается на хитрость. Он запускает программу, и если программа не завершается в течение определенного периода времени, она сохраняет свое состояние для последующего использования. Затем он запускает следующую программу, которая должна быть запущена. Существует несколько доступных алгоритмов, которые позволяют планировать, какой должна быть следующая программа: циклический перебор , взвешенный циклический перебор и т.д. Хорошо то, что все это хорошо абстрагируется от разработчика концепцией операционной системы поток . OS берет на себя всю тяжелую работу, включая сохранение состояния выполнения. Однако у потоков есть два недостатка:

  • Поток является тяжеловесным поскольку он несет в себе много состояния
  • Для создания потока требуется много машинных ресурсов

Во всех случаях современные ОС допускают M потоков, где M – это пара тысяч. Современные машины являются многоядерными, предлагая N ядер, что на два порядка меньше, чем M . Наличие нескольких ядер, будь то физических или виртуальных, принципиально не меняет базовый механизм: M намного выше, чем N , и ОС заботится о сопоставлении потока с ядром.

Блокирующие потоки

Приведенная выше модель хорошо работает в устаревших сценариях, но не так хорошо в веб-сценариях. Представьте себе веб-сервер, который должен ответить на HTTP-запрос. В не очень добрые старые времена CGI был одним из способов обработки запросов. Он сопоставлял каждый запрос с процессом, так что для обработки запроса требовалось создать совершенно новый процесс, и он был очищен после отправки ответа.

Серверы приложений Java EE значительно улучшили ситуацию, поскольку реализации сохраняли потоки в пуле для последующего повторного использования. Однако представьте, что генерация ответа требует времени например, потому что ему необходимо получить доступ к базе данных для чтения данных. Пока база данных не вернет данные, поток должен ждать.

Это означает, что потоки фактически ожидают большую часть своего срока службы На одной стороне, такие потоки сами по себе не используют CPU . С другой стороны, он использует другие виды ресурсов, в частности память.

Кроме того, слишком много потоков являются бременем для операционной системы: OS приходится балансировать огромное количество потоков на ограниченном количестве ядер процессора. Это требует драгоценных циклов процессора, и в результате ОС конкурирует за процессор с приложениями.

Теперь количество одновременных запросов может быть намного больше, чем количество потоков, доступных серверу. Следовательно, поток, который блокируется, является пустой тратой ресурсов и может сделать сервер невосприимчивым. Чтобы исправить это, можно добавить больше веб-серверов для обработки нагрузки: это горизонтальное масштабирование .

В большинстве случаев горизонтального масштабирования достаточно. Время, затраченное на ожидание блокирующих потоков, является пустой тратой, но не имеет связанных с этим затрат… если только чья-то инфраструктура не находится в “Облаке”. В этом случае кто-то платит за неиспользуемые ресурсы: это никогда не бывает разумной идеей.

Однопоточные, реактивные и сопрограммные модели Kotlin

Для разработчика управление несколькими потоками является сложной задачей. Например, если вы использовали (или разрабатывали) Swing apps, вы, вероятно, знаете об “эффекте серого прямоугольника”. Это происходит при взаимодействии пользователя с окном например, щелчок запускает длительную задачу. Если вы переместите другое окно поверх окна Swing, а затем переместите его, Swing не будет перерисовывать область, где другое окно пересекалось с окном Swing, оставляя уродливый серый прямоугольник. Причина в том, что длительная задача запускается в потоке отправки событий , вместо выделенного потока. И этого довольно легко избежать, даже не говоря о синхронизации в общем изменяемом состоянии!

Чтобы избежать этого, некоторые стеки полностью запрещают разработчикам использовать несколько потоков. Например, API для Node.js предлагает только один неблокирующий поток цикла событий: отправленные функции представлены в форме обратных вызовов. Обратите внимание, что это не мешает реализации использовать несколько потоков.

Подход Reactive – это еще одна альтернатива, на самом деле очень похожая. Хотя он избавляется от ограничения однопоточного API и предлагает механизм обратного давления, он по-прежнему требует неблокирующего кода. Поскольку потоки операционной системы являются дорогостоящими, Reactive объединяет их и повторно использует на протяжении всего жизненного цикла приложения. Основной процесс заключается в том, чтобы получить свободный поток из пула, позволить ему выполнить код, а затем отпустить поток обратно в пул. Вот почему для этого требуется неблокирующий код: если код блокируется, то исполняющий поток не будет освобожден, и пул будет исчерпан в тот или иной момент.

Я с интересом наблюдал за тем, как Реактивная модель распространилась по экосистеме Spring подобно костру, хотя я предпочел остаться в стороне. ИМХО, реактивный страдает от нескольких недостатков:

  • Установка на написание (и чтение!) реактивного кода сильно отличается от установки на написание традиционного кода. Я охотно признаю, что изменение своего мышления просто требует времени, продолжительность которого зависит от каждого разработчика.
  • Хотя настоящие разработчики не занимаются отладкой, я знаю многих, кто это делает, включая меня. Из-за описанной выше магии переключения потоков нелегко следить за потоком фрагмента кода с его связанным состоянием. Для этого требуется соответствующий инструментарий, такой как IntelliJ IDEA с соответствующим плагином . Наконец, по той же причине традиционные трассировки стека бесполезны. Некоторая черная магия позволяет обойти это. Однако это не для слабонервных. Для получения полного списка опций проверьте
  • этот документ .

Язык Kotlin предлагает альтернативу реактивному подходу: сопрограммы. Короче говоря, при использовании ключевого слова suspend компилятор Kotlin генерирует конечный автомат в байт-коде. Преимущество заключается в том, что функции, вызываемые в блоке сопрограммы, выглядят так, как будто они выполняются последовательно, хотя они выполняются параллельно – или, если быть более точным, потенциально могут быть, в зависимости от точной области.

Проектный станок и виртуальные потоки

Как реактивная модель, так и сопрограмма Kotlin добавляют дополнительный уровень абстракции между клиентским кодом и потоками JVM. Ответственность за динамическое сопоставление одного с другим лежит на фреймворке/библиотеке. Суть дела в том, что поток JVM представляет собой тонкую оболочку вокруг потока операционной системы : помните, что потоки операционной системы дороги в создании и ограничены в количестве парой тысяч.

Цель Project Loom – фактически отделить потоки JVM от потоков операционной системы. Когда я впервые узнал об этой инициативе, идея состояла в том, чтобы создать дополнительную абстракцию под названием Fiber (threads, Project Loom, вы улавливаете суть?). Ответственность Fiber заключалась в том, чтобы получить поток операционной системы, заставить его запускать код, а затем выпустить обратно в пул, точно так же, как это делает theReactivestack.

Текущее предложение имеет огромное отличие: вместо введения нового Fiber glass оно сокращает один класс, хорошо знакомый разработчикам Java – java.lang. Поток ! Следовательно, в новых версиях JVM некоторые объекты Thread могут быть тяжеловесными и сопоставляться с потоками ОС, в то время как некоторые могут быть виртуальными потоками.

Последствия

Главный вопрос заключается в том, что теперь, когда JVM API предлагает абстракцию над потоками ОС, что станет с другими абстракциями, такими как Reactive и сопрограммы? Я плохо разбираюсь в прогнозах, но вот некоторые возможные позиции компаний, стоящих за реактивными/сопрограммами:

  1. Результатом является то, что они понимают, что их фреймворки больше не приносят никакой добавленной стоимости и являются просто дублированием. Они прекращают свои усилия по разработке, предоставляя только версии для обслуживания существующим клиентам. Они помогают указанным клиентам перейти на новый Thread API, некоторая помощь может быть в форме платной консультации.
  2. Напротив, после того, как они вложили столько усилий в свои соответствующие структуры, они решают продолжать, как будто ничего не произошло. Например, Spring framework позаботился о фактическом проектировании общего реактивного API, называемого Реактивные потоки , без каких-либо зависимостей Spring. В настоящее время существует две реализации: RxJava v2 и Pivotal Project Reactor . Со своей стороны, JetBrains рекламирует сопрограммы Kotlin как самый простой способ параллельного запуска кода.
  3. Наконец, можно было бы найти золотую середину. Обе платформы продолжат свою жизнь, но изменят свою соответствующую базовую реализацию на использование виртуальных потоков .

Из-за заблуждения о заниженных затратах вариант № 1 крайне маловероятен: отдел продаж и маркетинга будет стремиться сохранить свое “конкурентное преимущество” – что бы это ни значило в их глазах. В то время как некоторые инженеры захотят сохранить существующий код по той же причине, некоторые другие будут настаивать на использовании нового API. Следовательно, я тоже не верю, что # 2 произойдет. Тем не менее, я думаю, что силовая игра между обеими инженерными фракциями, а затем между ними и маркетингом/продажами найдет баланс на # 3.

Вывод

Проект ткацких станков изменяет существующий Поток реализация от сопоставления потока операционной системы к абстракции, которая может либо представлять такой поток/| или виртуальный поток. Само по себе это интересный шаг на платформе, которая исторически придавала гораздо большее значение обратной совместимости по сравнению с инновациями. По сравнению с другими недавними версиями Java, эта функция действительно меняет правила игры. Разработчики в целом должны начать знакомиться с ним как можно скорее. Разработчики, которые собираются узнать о Reactive и сопрограммах, вероятно, должны сделать шаг назад и оценить, следует ли им вместо этого изучать новый Thread API - или нет.

👍 Благодарности:

Большое спасибо моему коллеге Яромиру Хамале за его подробный обзор!

Чтобы идти дальше:

Первоначально опубликовано на Фанат Java 21 июня 2020 года.

Оригинал: “https://dev.to/nfrankel/on-project-loom-the-reactive-model-and-coroutines-4355”