WedX - журнал о программировании и компьютерных науках

Как вы на самом деле управляете максимальным количеством веб-потоков, используя реактивное программирование Spring 5?

При использовании классического подхода Tomcat вы можете предоставить своему серверу максимальное количество потоков, которые он может использовать для обработки веб-запросов от пользователей. Используя парадигму реактивного программирования и Reactor в Spring 5, мы можем лучше масштабироваться по вертикали, обеспечивая минимальную блокировку.

Мне кажется, что это делает это менее управляемым, чем классический подход Tomcat, где вы просто определяете максимальное количество одновременных запросов. Когда у вас есть максимальное количество одновременных запросов, проще оценить максимальный объем памяти, который потребуется вашему приложению, и соответствующим образом масштабировать его. Когда вы используете реактивное программирование Spring 5, это кажется более хлопотным.

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


Ответы:


1

Нет блокировки ввода-вывода ВООБЩЕ

Прежде всего, если у вас нет какой-либо блокирующей операции, вам вообще не следует беспокоиться о том, сколько потоков нужно предоставить для управления параллелизмом. В этом случае у нас есть только один воркер, который обрабатывает все соединения асинхронно и неблокируя. И в этом случае мы можем легко масштабировать воркеров соединений, которые обрабатывают все соединения без конфликтов и когерентности (каждый воркер имеет свою собственную очередь полученных соединений, каждый воркер работает на своем процессоре), и мы можем лучше масштабировать приложение в этом случае ( ничего общего с дизайном).

Резюме: в этом случае вы управляете максимальным количеством веб-потоков так же, как и раньше, с помощью конфигурации приложения-контейнера (Tomcat, WebSphere и т. д.) или аналогичного в случае серверов без сервлетов, таких как Netty или гибридный Undertow. Преимущество - вы можете обрабатывать мууууууу больше запросов пользователей, но с тем же потреблением ресурсов.

Блокирующая база данных и неблокирующий веб-API (например, WebFlux через Netty).

В случае, если нам нужно как-то справиться с блокировкой ввода-вывода, для мгновенной связи с БД через блокировку JDBC, наиболее подходящий способ сохранить масштабируемость и эффективность вашего приложения, насколько это возможно, мы должны использовать выделенный пул потоков для ввода-вывода.

Требования к пулу потоков

Прежде всего, мы должны создать пул потоков с точно таким же количеством рабочих процессов, сколько доступно соединений в пуле соединений JDBC. Следовательно, у нас будет ровно такое же количество потоков, которые будут блокировать ожидание ответа, и мы используем наши ресурсы настолько эффективно, насколько это возможно, поэтому память для стека потоков не будет потребляться больше, чем это действительно необходимо (В других слово Поток на модель соединения).

Как настроить пул потоков в соответствии с размером пула соединений

Поскольку доступ к свойствам различается для конкретной базы данных и драйвера JDBC, мы всегда можем внедрить эту конфигурацию в конкретное свойство, что, в свою очередь, означает, что оно может быть настроено devops или системным администратором. Конфигурация Threadpool (в нашем примере это настройка планировщика Project Reactor 3) может выглядеть следующим образом:

@Configuration
public class ReactorJdbcSchedulerConfig {
    @Value("my.awasome.scheduler-size")
    int schedulerSize;

    @Bean
    public Scheduler jdbcScheduler() {
        return Schedulers.fromExecutor(new ForkJoinPool(schedulerSize));
        // similarly 
        // ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // taskExecutor.setCorePoolSize(schedulerSize);
        // taskExecutor.setMaxPoolSize(schedulerSize);
        // taskExecutor.setQueueCapacity(schedulerSize);
        // taskExecutor.initialize();
        // return Schedulres.fromExecutor(taskExecutor);
    }
}
...

    @Autowire
    Scheduler jdbcScheduler;


    public Mono myJdbcInteractionIsolated(String id) {
         return Mono.fromCallable(() -> jpaRepo.findById(id))
                    .subscribeOn(jdbcScheduler)
                    .publishOn(Schedulers.single());
    }
...

Как можно заметить, с помощью этого метода мы можем делегировать нашу общую конфигурацию пула потоков внешней команде (например, системным администраторам) и позволять им управлять потреблением памяти, которая используется для созданных потоков Java.

Держите блокирующий пул потоков ввода-вывода только для работы ввода-вывода.

Этот оператор означает, что поток ввода-вывода должен быть только для операций, которые ожидают блокировки. В свою очередь, это означает, что после того, как поток дождался ответа, вы должны перенести обработку результата в другой поток.

Вот почему в приведенном выше фрагменте кода я поставил .publishOn сразу после .subscribeOn.

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

Наконец, API блокировки (Spring MVC) и блокировка ввода-вывода (доступ к базе данных)

В этом случае реактивная парадигма вообще не нужна, так как вы не получаете от этого никакой прибыли. Прежде всего, реактивное программирование требует особого изменения мышления, особенно в понимании использования функциональных методов с реактивными библиотеками, такими как RxJava или Project Reactor. В свою очередь, для неподготовленных пользователей это усложняет задачу и вызывает больше вопросов "Что ****** здесь происходит???". Итак, в случае блокировки операций с обоих концов, вам следует дважды подумать, действительно ли вам здесь нужно реактивное программирование.

Кроме того, бесплатного волшебства не бывает. Реактивные расширения имеют большую внутреннюю сложность, и, используя все эти волшебные .map, .flatMap и т. д., вы можете потерять общую производительность и потребление памяти вместо того, чтобы выиграть, как в случае сквозного неблокирующего асинхронного взаимодействия.

Это означает, что старое доброе императивное программирование здесь будет более подходящим, и будет намного проще контролировать размер вашего приложения в памяти, используя старое доброе управление конфигурацией Tomcat.

03.01.2018

2

Можете ли вы попробовать это:

public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(15);
        taskExecutor.setMaxPoolSize(100);
        taskExecutor.setQueueCapacity(100);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

Это работает для асинхронного режима весной 4, но я не уверен, что он будет работать весной 5 с реактивным.

03.08.2017
Новые материалы

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


Для любых предложений по сайту: [email protected]