В этой статье рассматривается наше решение отказаться от реактивной архитектуры в нашем программном проекте. Мы углубимся в основные принципы реактивных систем, преимущества неблокирующего ввода-вывода и проблемы, с которыми мы столкнулись при реактивном подходе.
Реактивный включает в себя набор принципов и рекомендаций, направленных на создание адаптивных распределенных систем и приложений, характеризующихся:
Одним из ключевых преимуществ реактивных систем является использование неблокирующего ввода-вывода. Этот подход позволяет избежать блокировки потоков во время операций ввода-вывода, позволяя одному потоку обрабатывать несколько запросов одновременно. Это может значительно повысить эффективность системы по сравнению с традиционным блокирующим вводом-выводом.
В традиционной многопоточности операции блокировки создают серьезные проблемы при оптимизации систем (Рис. 1). Жадные приложения, потребляющие слишком много памяти, неэффективны и наказывают другие приложения, часто вызывая необходимость запроса дополнительных ресурсов, таких как память, процессор или более крупные виртуальные машины.
Рис. 1. Традиционная многопоточность
Операции ввода-вывода являются неотъемлемой частью современных систем, и эффективное управление ими имеет первостепенное значение для предотвращения жадного поведения. Реактивные системы используют неблокирующий ввод-вывод, позволяя небольшому количеству потоков ОС обрабатывать многочисленные одновременные операции ввода-вывода.
Хотя неблокирующий ввод-вывод дает существенные преимущества, он вводит новую модель выполнения, отличную от традиционных платформ. Для решения этой проблемы появилось реактивное программирование, поскольку оно снижает неэффективность простоя потоков платформы во время операций блокировки (Рис. 2).
Рис. 2. Реактивный цикл обработки событий
Quarkus использует реактивный механизм на базе Eclipse Vert.x и Netty, облегчающий неблокирующее взаимодействие ввода-вывода. Mutiny, предпочтительный подход к написанию реактивного кода с помощью Quarkus, использует парадигму, управляемую событиями, в которой реакции запускаются полученными событиями.
Mutiny предлагает два событийно-ориентированных и ленивых типа:
Хотя реактивные системы обладают преимуществами, во время разработки мы столкнулись с рядом проблем:
»Действительно, соотношение времени, затрачиваемого на чтение и запись, значительно превышает 10 к 1. Мы постоянно читаем старый код, пытаясь написать новый код. ...[Следовательно] облегчение чтения делает его более простым для чтения. легче писать."
― Роберт К. Мартин, Чистый код: справочник по гибкому мастерству разработки программного обеспечения
Вот пример реактивного кода с использованием Mutiny, чтобы проиллюстрировать сложность:
Multi.createFrom().ticks().every(Duration.ofSeconds(15)) .onItem().invoke(() - > Multi.createFrom().iterable(configs()) .onItem().transform(configuration - > { try { return Tuple2.of(openAPIConfiguration, RestClientBuilder.newBuilder() .baseUrl(new URL(configuration.url())) .build(MyReactiveRestClient.class) .getAPIResponse()); } catch (MalformedURLException e) { log.error("Unable to create url"); } return null; }).collect().asList().toMulti().onItem().transformToMultiAndConcatenate(tuples - > { AtomicInteger callbackCount = new AtomicInteger(); return Multi.createFrom().emitter(emitter - > Multi.createFrom().iterable(tuples) .subscribe().with(tuple - > tuple.getItem2().subscribe().with(response - > { emitter.emit(callbackCount.incrementAndGet()); if (callbackCount.get() == tuples.size()) { emitter.complete(); } }) )); }).subscribe().with(s - > {}, Throwable::printStackTrace, () - > doSomethingUponComplete())) .subscribe().with(aLong - > log.info("Tic Tac with iteration: " aLong));
Project Loom, недавняя разработка в экосистеме Java, обещает смягчить проблемы, связанные с операциями блокировки. Предоставляя возможность создавать тысячи виртуальных потоков без изменений оборудования, Project Loom потенциально может устранить необходимость в реактивном подходе во многих случаях.
"Project Loom убьет реактивное программирование"
―Брайан Гетц
В заключение, наше решение отойти от стиля реактивной архитектуры и перейти к прагматическому подходу к долгосрочной ремонтопригодности нашего проекта. Хотя реактивные системы предлагают потенциальные преимущества, проблемы, которые они представляют для нашей команды, перевешивают эти преимущества в нашем конкретном контексте.
Важно, что этот сдвиг не повлиял на производительность. Это положительный результат, поскольку он демонстрирует, что хорошо спроектированная нереактивная (императивная) архитектура может обеспечить необходимую производительность без сложностей, связанных с реактивной архитектурой в нашем случае.
Глядя в будущее, мы по-прежнему фокусируемся на создании базы кода, которая не только функциональна, но и проста для понимания и поддержки для разработчиков любого уровня опыта. Это не только сокращает время разработки, но и способствует лучшему сотрудничеству и обмену знаниями внутри команды.
На графике ниже ось X представляет растущую сложность нашей кодовой базы по мере ее развития, а ось Y отображает время, необходимое для этих изменений в разработке.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3