|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Всем привет, помогите разобраться в работе @Async в спринге. В документации пишут: Код: xml 1. 2. 3.
This changes the thread pool to use a bounded queue so that when the queue is full (100 tasks), the thread pool increases to maximum 16 threads. Shrinking of the pool is more aggressive as threads are reclaimed when they are idle for 10 seconds (rather than 60 seconds by default). То-есть когда очередь заполняется до 100 задач пул потоков должен увеличиваться до значения max-size, правильно понимаю ? Мой конфиг: Код: xml 1. 2. 3. 4. 5. 6. 7. 8.
Отправляю 10 задач, при этом пул не расширяется, как было изначально 2 активных потока, они и используются, задачи висят в очереди и ждут освобождения потоков. Что не так делаю ? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 11:46 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Вроде разобрался. queue-capacity указывает на то, какое количество задач могут висеть в очереди и ждать освобождение потоков, а для всех тех задач, которые в эту очередь не попали, создаются новые потоки до размера max-size То-есть у нас есть 20 задач, queue-capacity допустим 5 и max-size 20. При запуске 5 задач встали в очередь, а для остальных создался пул в 15 потоков. 15 задач выполняются параллельно, а 5 висят в очереди и ждут освобождения потоков. Все правильно ?) ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 12:08 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
А что будет если 100 задач висят а ты закидываешь еще одну? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 14:16 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
bobo96 Вроде разобрался. queue-capacity указывает на то, какое количество задач могут висеть в очереди и ждать освобождение потоков, а для всех тех задач, которые в эту очередь не попали, создаются новые потоки до размера max-size То-есть у нас есть 20 задач, queue-capacity допустим 5 и max-size 20. При запуске 5 задач встали в очередь, а для остальных создался пул в 15 потоков. 15 задач выполняются параллельно, а 5 висят в очереди и ждут освобождения потоков. Все правильно ?) Очередь может без ограничений размеров либо с ограничением. Ограничение позволяет не растить очередь до бесконечности - а, к примеру, выбрасывать исключение если достигли максимума. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 15:05 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
bobo96, вроде как в жавадоке по java.util.concurrent.ThreadPoolExecutor все есть: ThreadPoolExecutor Queuing Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing: - If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing. - If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread. - If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected. There are three general strategies for queuing: 1. Direct handoffs. A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed. 2. Unbounded queues. Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server. While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed. 3. Bounded queues. A bounded queue (for example, an ArrayBlockingQueue) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput. Rejected tasks New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) method of its RejectedExecutionHandler. Four predefined handler policies are provided: 1. In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection. 2. In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted. 3. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped. 4. In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.) It is possible to define and use other kinds of RejectedExecutionHandler classes. Doing so requires some care especially when policies are designed to work only under particular capacity or queuing policies. бут его только конфигурит, причем евойные умолчания отличаются от спринговых ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 15:57 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Жалко тратить пул потоков на одну единсвенную роль в проекте. Это наверно удобно для узких микро-сервисов которые делают строго одну задачу. Но так... универсальности нет. Я-бы лучше конфигурил из Java. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.01.2022, 16:52 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Очередь может без ограничений размеров либо с ограничением. Ограничение позволяет не растить очередь до бесконечности - а, к примеру, выбрасывать исключение если достигли максимума. Я это место никогда не понимал и до сих пор не понимаю: вот если мы уже проделали некоторую полезную работу, а потом решили что нужно что-то выполнить асинхронно, а нам на submit выкидывают RejectedExecutionException - что с этим исключением делать? как его компенсировать? меня вот такие идеи посещают: - выполнять задачу в текущем потоке неверно по следующим соображениям: -- фактически таким образом мы никак не накладываем ограничения на количество одновременных потоков, т.е. смысл очереди как таковой теряется -- мы проигрываем в throughput поскольку вместо того чтобы планировать задачи мы какую-то их часть выполняем сами, и имеем возможность планировать только после завершения -- существуют сценарии, в которых имеет значение выполняется ли задача в другом потоке или в текущем, например использование @Transactional может приводить к разным результатам в зависимости от (в случае разных потоков мы видим разные данные, а в случае одного - одни и те же), уже не говоря о том, что у пула потоков может быть другой класслоадер - иметь персистентную очередь с более "разумными ограничениями" на ее размер - здесь получается, что вся затея с потоками нам на самом деле и не нужна, а нужен изначально более надежный механизм - накладывать ограничения где-то вначале стэка - однако это мало чем отличается от синхронного выполнения ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 10:12 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Stanislav Bashkyrtsev Очередь может без ограничений размеров либо с ограничением. Ограничение позволяет не растить очередь до бесконечности - а, к примеру, выбрасывать исключение если достигли максимума. Я это место никогда не понимал и до сих пор не понимаю: вот если мы уже проделали некоторую полезную работу, а потом решили что нужно что-то выполнить асинхронно, а нам на submit выкидывают RejectedExecutionException - что с этим исключением делать? как его компенсировать? меня вот такие идеи посещают: - выполнять задачу в текущем потоке неверно по следующим соображениям: -- фактически таким образом мы никак не накладываем ограничения на количество одновременных потоков, т.е. смысл очереди как таковой теряется -- мы проигрываем в throughput поскольку вместо того чтобы планировать задачи мы какую-то их часть выполняем сами, и имеем возможность планировать только после завершения -- существуют сценарии, в которых имеет значение выполняется ли задача в другом потоке или в текущем, например использование @Transactional может приводить к разным результатам в зависимости от (в случае разных потоков мы видим разные данные, а в случае одного - одни и те же), уже не говоря о том, что у пула потоков может быть другой класслоадер - иметь персистентную очередь с более "разумными ограничениями" на ее размер - здесь получается, что вся затея с потоками нам на самом деле и не нужна, а нужен изначально более надежный механизм - накладывать ограничения где-то вначале стэка - однако это мало чем отличается от синхронного выполнения По-разному можно: https://www.baeldung.com/java-rejectedexecutionhandler ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 12:57 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
guest2, вы проблему не осознали - проходите мимо. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 13:04 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Stanislav Bashkyrtsev Очередь может без ограничений размеров либо с ограничением. Ограничение позволяет не растить очередь до бесконечности - а, к примеру, выбрасывать исключение если достигли максимума. Я это место никогда не понимал и до сих пор не понимаю: вот если мы уже проделали некоторую полезную работу, а потом решили что нужно что-то выполнить асинхронно, а нам на submit выкидывают RejectedExecutionException - что с этим исключением делать? как его компенсировать? меня вот такие идеи посещают: - выполнять задачу в текущем потоке неверно по следующим соображениям: -- фактически таким образом мы никак не накладываем ограничения на количество одновременных потоков, т.е. смысл очереди как таковой теряется -- мы проигрываем в throughput поскольку вместо того чтобы планировать задачи мы какую-то их часть выполняем сами, и имеем возможность планировать только после завершения Сам я так никогда не делал, ну могу себе представить ситуацию. Андрей Панфилов - иметь персистентную очередь с более "разумными ограничениями" на ее размер - здесь получается, что вся затея с потоками нам на самом деле и не нужна, а нужен изначально более надежный механизм Ну и остается вариант с такими условиями: - Ресурсов не хватает - почему-то не можем использовать пользовательский поток (например, из-за той же ORM сессии) - и при этом не сохраняем задачи в БД. Например, потому что мы делали простой вариант на скорую руку. И в таком случае бросать исключение - это адекватная реакция. Говорят что очередь редко бывает заполнена наполовину - она либо пустая потому что задач нет, либо заполнена под завязку потому что мы не справляемся с нагрузкой. А если не справляемся, то лучше вернуть ошибку нежели растить очередь и в итоге подвиснуть на неопределенное время. Андрей Панфиловуже не говоря о том, что у пула потоков может быть другой класслоадерА зачем такое может понадобиться? ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 13:38 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов guest2, вы проблему не осознали - проходите мимо. В конце концов есть shutdown(). Тоже с этим исключением. Другое дело что по умолчанию не рекомендуется) ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 14:01 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Я это место никогда не понимал и до сих пор не понимаю Существует ресурс (потоки исполнения), который по тем или иным (не)обоснованными причинам считается дефицитным. Например, это может быть "бутылочное горлышко" в виде базы данных, которая "точно не выдержит" более определённого количества (одновременных) подключений. Существует поток задач, пик которых превышает доступное количество потоков исполнения. Выделенного количество потоков исполнения достаточно для усреднённого потока задач и мы согласны на дополнительные ожидания при пиковой нагрузке. В этой ситуации решением будет организация очереди задач, а "встроенный" пул просто берёт на себя всю рутину. Если "пула не хватило", то предложенная нагрузка превысила пропускную способность. Произошло это из-за (субъективной) нехватки потоков исполнения ("пожлобились") или из-за проблем производительности внешней (под)системы (выросло время исполнения задач) - надо смотреть отдельно. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 14:11 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Если все ради чего мы делаем @Async - это ускорить выполнение обработки пользовательского запроса (а затем мы ждем чтоб основная обработка и параллельная завершились и только потом отвечаем пользователю), то использовать тот же поток имеет смысл. Это значит что мы просто выполнили обе операции в одном потоке и пользователь дольше ждал. При этом кол-во потоков в приложении все равно ограничивается пулом в апп сервере. Сам я так никогда не делал, ну могу себе представить ситуацию. ну вот как по мне вполне стандартная ситуация во всяких BPM: пользователь выбрал каким-то образом пачку "задач" и жмякнул "завершить", само по себе "завершение задачи" может под собой подразумевать довольно длительные активности (выбор следующего исполнителя, создание задачи для него, запись аудита, отправка уведомлений и т.д. - полный фарш) поэтому есть два стандартных выхода из ситуации: - изначально правильно рисовать БП: т.е. чтобы при завершении задачи она действительно завершалась (пользователь ее уже не видел), а весь остальной процессинг шел бы асинхронно (это уже история про персистетную очередь) - вот так на самом деле мало кто умеет делать, особенно учитывая расхожий миф, что диаграммы BPM должны рисовать аналитики - пытаться что-то мутить с асинхронностью, но тут грабли Stanislav Bashkyrtsev Если задача стоит так, то я бы изначально просто в БД поместил задачу, и уже фоновая жоба бы ее считывала. И в таком варианте мы не переживаем насчет разрастания очереди. На моей практике это самый типичный сценарий. Можно даже совмещать - складываем в БД и сразу же сабмитим на асинхронное выполнение. Если все хорошо, то задача быстро выполнится, а если свет погасят - при следующем старте жоба вычитает задачу сама. Ограничиваем такой пул одним потоком и получаем что фоновая жоба и ручной сабмит никогда не работают одновременно. Офигенная конечно история, проблема только в том, что так "никто" изначально не делает, а при возникновении трудностей начинают городить костыли, которые в последующем очень сложно загребать Stanislav Bashkyrtsev Ну и остается вариант с такими условиями: - Ресурсов не хватает - почему-то не можем использовать пользовательский поток (например, из-за той же ORM сессии) - и при этом не сохраняем задачи в БД. Например, потому что мы делали простой вариант на скорую руку. И в таком случае бросать исключение - это адекватная реакция. Говорят что очередь редко бывает заполнена наполовину - она либо пустая потому что задач нет, либо заполнена под завязку потому что мы не справляемся с нагрузкой. А если не справляемся, то лучше вернуть ошибку нежели растить очередь и в итоге подвиснуть на неопределенное время. Stanislav Bashkyrtsev Андрей Панфиловуже не говоря о том, что у пула потоков может быть другой класслоадер ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 14:30 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Stanislav Bashkyrtsev Очередь может без ограничений размеров либо с ограничением. Ограничение позволяет не растить очередь до бесконечности - а, к примеру, выбрасывать исключение если достигли максимума. Я это место никогда не понимал и до сих пор не понимаю: вот если мы уже проделали некоторую полезную работу, а потом решили что нужно что-то выполнить асинхронно, а нам на submit выкидывают RejectedExecutionException - что с этим исключением делать? как его компенсировать? 100% согласен. Я-бы вообще не накладывал ограничений на длину очереди задач. Вот сколько памяти - столько пускай и растет. Но алёрты и мониторинг на PROD должны быть выставлены в какой-то 99й процентиль очереди. Пускай превышение очереди будет просто сигналом о другой проблеме. Например о том что пул не справляется. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 14:47 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Basil A. Sidorov Если рассуждать в терминах "предложенная нагрузка" и "пропускная способность" - всё вполне очевидно. Существует ресурс (потоки исполнения), который по тем или иным (не)обоснованными причинам считается дефицитным. Например, это может быть "бутылочное горлышко" в виде базы данных, которая "точно не выдержит" более определённого количества (одновременных) подключений. Существует поток задач, пик которых превышает доступное количество потоков исполнения. Выделенного количество потоков исполнения достаточно для усреднённого потока задач и мы согласны на дополнительные ожидания при пиковой нагрузке. В этой ситуации решением будет организация очереди задач, а "встроенный" пул просто берёт на себя всю рутину. Если "пула не хватило", то предложенная нагрузка превысила пропускную способность. Произошло это из-за (субъективной) нехватки потоков исполнения ("пожлобились") или из-за проблем производительности внешней (под)системы (выросло время исполнения задач) - надо смотреть отдельно. Это все круто звучит только когда "дефицитный ресурс" существует только в одной ипостаси (причем действительность говорит о том, что даже такие простые вещи не особо до всех доходят, к примеру есть ролик от Oracle говорящий о том, что вот никак нельзя в БД пихать больше запросов чем она может выдержать: ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 14:52 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
mayton Андрей Панфилов пропущено... Я это место никогда не понимал и до сих пор не понимаю: вот если мы уже проделали некоторую полезную работу, а потом решили что нужно что-то выполнить асинхронно, а нам на submit выкидывают RejectedExecutionException - что с этим исключением делать? как его компенсировать? 100% согласен. Я-бы вообще не накладывал ограничений на длину очереди задач. Вот сколько памяти - столько пускай и растет. Но алёрты и мониторинг на PROD должны быть выставлены в какой-то 99й процентиль очереди. Пускай превышение очереди будет просто сигналом о другой проблеме. Например о том что пул не справляется. 1. Размер тред пула и БД пула = N 2. Приходят к нам 2N людей: N обрабатывается, N в очереди. 3. Из-за того что мы сильно нагружены запросы выполняются долго, пока до второй половины людей доходит - они уже успели сделать рефреш страницы. На момент завершения первых N запросов, вторые N уже никому не нужны потому как все сделали рефреш и не ждут ответа. Но сервер-то об этом узнает только когда ответ начнет писать, соответственно он все равно будет обрабатывать запросы. 4. Пользователь уже злится и рефрешит как черт. Кол-во запросов все растет и растет, новые пользователи пришли. 5. Теперь даже если все ушли и прекратили работать с приложением, то очередь все равно уже 100N. Это значит что приложение оживет только через час когда обработает все 100N. А теперь допустим мы установили ограничение у очереди в N. Это значит что еще в самом начале много новых запросов отвалятся с ошибкой, и очередь не вырастет. И если нагрузка стихнет, то мы спокойно продолжим пользоваться приложением. Андрей ПанфиловОфигенная конечно история, проблема только в том, что так "никто" изначально не делает, а при возникновении трудностей начинают городить костыли, которые в последующем очень сложно загребатьНу значит меня можно занести в список "никто". Я в основном так и делаю асинхронщину :) Но мне не оч понятно к чему ты клонишь - вроде как говоришь что разработчики все делают плохо, но при этом ругаешься на то что в такой ситуации фича по ограничению очереди плохо работает. Но только лишь настройками пула мы не можем починить всю кривизну проекта.. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 15:23 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Ну и получаем такую ситуацию (это из реального опыта): 1. Размер тред пула и БД пула = N 2. Приходят к нам 2N людей: N обрабатывается, N в очереди. 3. Из-за того что мы сильно нагружены запросы выполняются долго, пока до второй половины людей доходит - они уже успели сделать рефреш страницы. На момент завершения первых N запросов, вторые N уже никому не нужны потому как все сделали рефреш и не ждут ответа. Но сервер-то об этом узнает только когда ответ начнет писать, соответственно он все равно будет обрабатывать запросы. 4. Пользователь уже злится и рефрешит как черт. Кол-во запросов все растет и растет, новые пользователи пришли. 5. Теперь даже если все ушли и прекратили работать с приложением, то очередь все равно уже 100N. Это значит что приложение оживет только через час когда обработает все 100N. Это неправильный юзкейс работы с БД. В стеке должна стоять не очередь а наоборот CirquitBreaker. Пускай как можно быстрее получит плитку что "Технические issues" и повторит попозже. Получать любой ценой респонс из БД - нельзя. Любой базовик скажет что при любых проблемах нагрузки надо строчно отстреливать сесии по kill session а никак не плодить новые. В варианте использования пула задач и jdbc Connection нету людей. Там - бизнес задачи. Которые могут долго ждать и работать в фоне. Для них екзекютор - самое то. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 15:47 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Ну и получаем такую ситуацию (это из реального опыта): 1. Размер тред пула и БД пула = N 2. Приходят к нам 2N людей: N обрабатывается, N в очереди. 3. Из-за того что мы сильно нагружены запросы выполняются долго, пока до второй половины людей доходит - они уже успели сделать рефреш страницы. На момент завершения первых N запросов, вторые N уже никому не нужны потому как все сделали рефреш и не ждут ответа. Но сервер-то об этом узнает только когда ответ начнет писать, соответственно он все равно будет обрабатывать запросы. 4. Пользователь уже злится и рефрешит как черт. Кол-во запросов все растет и растет, новые пользователи пришли. 5. Теперь даже если все ушли и прекратили работать с приложением, то очередь все равно уже 100N. Это значит что приложение оживет только через час когда обработает все 100N. А теперь допустим мы установили ограничение у очереди в N. Это значит что еще в самом начале много новых запросов отвалятся с ошибкой, и очередь не вырастет. И если нагрузка стихнет, то мы спокойно продолжим пользоваться приложением. вроде как базовый синхронный сценарий должен правильно ситуацию обрабатывать: - в JDBC размер пула N - в коте размер HTTP пула N/2 (на случай если мы где-то хотим какие-то выкрутасы делать) - все что в размер HTTP пула не влезает тупо ждет - на refresh браузер шлет RST и то что ждет на бэке само отваливается (я тут за произвольный браузер и F5 ручаться не готов, однако разработчики фронта мне показывали, что при должном желании можно честно оборвать висящий XHR запрос: https://stackoverflow.com/questions/24258279/internals-client-and-server-of-aborting-an-xmlhttprequest - если инфраструктура умеет мозг включать, то XHR abort может даже до БД дойти в виде OOB и оно и там запрос прервет) Андрей ПанфиловНу значит меня можно занести в список "никто". Я в основном так и делаю асинхронщину :) Но мне не оч понятно к чему ты клонишь - вроде как говоришь что разработчики все делают плохо, но при этом ругаешься на то что в такой ситуации фича по ограничению очереди плохо работает. Но только лишь настройками пула мы не можем починить всю кривизну проекта.. на мой взгляд где-то в документации должно быть четко (большими жирными буквами) описано, что определенное API может на самом усугублять проблему, от которой пытаются избавиться за счет использования этого API, а сейчас мы имеем нечто в духе: вот здесь можно сделать пул, он позволяет что-то обрабатывать параллельно и все слово "параллельно" читают как "быстрее" вместо "непредсказуемо". Из той же оперы: есть CompletableFuture, у него есть #thenApply - где этот thenApply выполняется - хрен его знает ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 16:04 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev А теперь допустим мы установили ограничение у очереди в N. Это значит что еще в самом начале много новых запросов отвалятся с ошибкой, и очередь не вырастет. И если нагрузка стихнет, то мы спокойно продолжим пользоваться приложением. вот это N мы где выставили-то? Вот у нас два шага: - что-то успешно записали в БД (тут один пул) - отправили куда-то сообщение, что нужно сбросить кеши, перечитать и т.п. (здесь второй пул) ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 16:09 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Oracle (DBMS) кстати хорошо развил задачу управления ресурсами (Resource management) для schedulers, jobs e.t.c. Задачи имеют профили нагрузки и окна исполнения. Например в ночное время можно гонять статистику, ETL, и машинное обучение. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 16:17 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Это все круто звучит только когда "дефицитный ресурс" существует только в одной ипостаси Если есть ограниченный пул потоков исполнения и неподконтрольный поток задач, то требуется изначально быть готовым к ситуации "гипс снимают, клиент уезжает". Сразу обламывать тех, кому не повезло, выкидывать "неудачников" из очереди или "положиться на русский авось" в виде исключения - это уж "што шмогла". ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 16:21 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов Stanislav Bashkyrtsev Ну и получаем такую ситуацию (это из реального опыта): 1. Размер тред пула и БД пула = N 2. Приходят к нам 2N людей: N обрабатывается, N в очереди. 3. Из-за того что мы сильно нагружены запросы выполняются долго, пока до второй половины людей доходит - они уже успели сделать рефреш страницы. На момент завершения первых N запросов, вторые N уже никому не нужны потому как все сделали рефреш и не ждут ответа. Но сервер-то об этом узнает только когда ответ начнет писать, соответственно он все равно будет обрабатывать запросы. 4. Пользователь уже злится и рефрешит как черт. Кол-во запросов все растет и растет, новые пользователи пришли. 5. Теперь даже если все ушли и прекратили работать с приложением, то очередь все равно уже 100N. Это значит что приложение оживет только через час когда обработает все 100N. А теперь допустим мы установили ограничение у очереди в N. Это значит что еще в самом начале много новых запросов отвалятся с ошибкой, и очередь не вырастет. И если нагрузка стихнет, то мы спокойно продолжим пользоваться приложением. вроде как базовый синхронный сценарий должен правильно ситуацию обрабатывать: - в JDBC размер пула N - в коте размер HTTP пула N/2 (на случай если мы где-то хотим какие-то выкрутасы делать) - все что в размер HTTP пула не влезает тупо ждет - на refresh браузер шлет RST и то что ждет на бэке само отваливается (я тут за произвольный браузер и F5 ручаться не готов, однако разработчики фронта мне показывали, что при должном желании можно честно оборвать висящий XHR запрос: https://stackoverflow.com/questions/24258279/internals-client-and-server-of-aborting-an-xmlhttprequest - если инфраструктура умеет мозг включать, то XHR abort может даже до БД дойти в виде OOB и оно и там запрос прервет) Однако у томката есть две отдельные настройки: maxConnections & acceptCount (socket backlog). Если maxConnections == threadPool.size(), то вся очередь запросов будет торчать на уровне ОС - соединения просто не будут приниматься. И в таком случае да - как только освобождается поток и делает accept(), то получаем handshake, и мы и правда должны получить ошибку сразу. Но это мы все равно приходим к тому чтоб ограничить очередь (во всяком случае в томкате) - в данном случае в 0. Кстати, если у кого-то есть идеи зачем вообще нужен maxConnections отличный от размера thread pool'a - было бы интересно услышать. Андрей Панфилов Stanislav Bashkyrtsev Ну значит меня можно занести в список "никто". Я в основном так и делаю асинхронщину :) Но мне не оч понятно к чему ты клонишь - вроде как говоришь что разработчики все делают плохо, но при этом ругаешься на то что в такой ситуации фича по ограничению очереди плохо работает. Но только лишь настройками пула мы не можем починить всю кривизну проекта.. на мой взгляд где-то в документации должно быть четко (большими жирными буквами) описано, что определенное API может на самом усугублять проблему, от которой пытаются избавиться за счет использования этого API, а сейчас мы имеем нечто в духе: вот здесь можно сделать пул, он позволяет что-то обрабатывать параллельно и все слово "параллельно" читают как "быстрее" вместо "непредсказуемо". Из той же оперы: есть CompletableFuture, у него есть #thenApply - где этот thenApply выполняется - хрен его знаетНе, ну если речь про ликбез и доки, то может и можно что-то улучшать. Только чем толще дока, тем ее меньше читают :) Андрей Панфилов Stanislav Bashkyrtsev А теперь допустим мы установили ограничение у очереди в N. Это значит что еще в самом начале много новых запросов отвалятся с ошибкой, и очередь не вырастет. И если нагрузка стихнет, то мы спокойно продолжим пользоваться приложением. вот это N мы где выставили-то? Вот у нас два шага: - что-то успешно записали в БД (тут один пул) - отправили куда-то сообщение, что нужно сбросить кеши, перечитать и т.п. (здесь второй пул) ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 17:30 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов, Не верю, что вы не можете придумать юз кейс с дефицитным ресурсом. Если вы в движке BPM ограничите пул на три задачи и четвертая будет райзе, то так тому и быть. Пусть идет и перепроектирует. Или вы хотите чтобы он 500 задачек за раз понакидал и жди их до пенсии. Оба варианта имеют право на жизнь. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 20:10 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Stanislav Bashkyrtsev Если мы приняли соединение, не верю что это сработает. Да, мы получим ошибку когда начнем писать в сокет потому что тот окажется закрытым. Но это произойдет уже в конце, когда мы провели всю работу с БД. Чтоб суметь оборвать БД запрос - это нужно в самом Томкате знать про БД (и если бы это и могло работать, то только с пулом самого томката), в общем звучит unreal. Вы не верите только из-за того, что разработчики редко используют фичу, уже ставшую де-факто стандартом :) (Вади не хватает, он бы все здесь разрулил) Я специально проверил и повторно убедился что оно работает, а именно: - создал сервлет с Thread.sleep() - вызвал его из curl, жамкнул Ctrl+C - в org.apache.tomcat.util.net.SocketWrapperBase#close приехала инфа, что клиент разорвал соединение - вызвал из браузера и закрыл браузер - в org.apache.tomcat.util.net.SocketWrapperBase#close опять же приехала инфа, что клиент разорвал соединение однако прежде чем бросаться проверять, убедитесь, что в коте врублен http/2 и настроен ssl, и вы хорошо дунули, а то фокуса не получится. Прерывать запросы в БД после отвала клиента - это уже дело техники, нужно всего-то уметь Statement#executeXXX ассоциировать с потоком. ... |
|||
:
Нравится:
Не нравится:
|
|||
05.01.2022, 22:35 |
|
Spring @Async - увеличение пула в реальном времени ?
|
|||
---|---|---|---|
#18+
Андрей Панфилов , я о таком не знал, спасибо. Видать помогает то что это NIO и мы получаем явное событие о закрытии (я обязательно попробую как будет не лень). Ну ладно. Но это результат не меняет: 1. Судя по коду там нет никаких хуков, т.е. Томкат никак об этом не может оповестить нас. 2. А сам он в любом случае не смог бы закрыть Statement. Я вот использую C3P0 - Томкат же ничего об этом не знает, у него нет ссылок на эти объекты. Но в теории да, выходит такое реализуемо. Особенно если бы мы использовали томкатовский пул. ... |
|||
:
Нравится:
Не нравится:
|
|||
06.01.2022, 00:01 |
|
|
start [/forum/topic.php?fid=59&msg=40124541&tid=2120269]: |
0ms |
get settings: |
4ms |
get forum list: |
7ms |
check forum access: |
1ms |
check topic access: |
1ms |
track hit: |
38ms |
get topic data: |
3ms |
get forum data: |
1ms |
get page messages: |
426ms |
get tp. blocked users: |
0ms |
others: | 2490ms |
total: | 2971ms |
0 / 0 |