Чтобы разрабатывать эффективные с точки зрения производительности сервисы на Oracle Service Bus, необходимо понимать как данная шина использует потоки.
Общая модель потоков OSB, т.е. модель потоков при использовании маршрутизации, следующая: ветвь обработки запроса выполняется в одном потоке, потоке для WorkManager'а прокси-сервиса, ветвь обработки ответа - в другом потоке - потоке для WorkManager'а бизнес-сервиса. После отправки запроса вызываемому сервису поток прокси-сервиса возвращается в пул потоков. Существует специальный мультиплексор, muxer, который используется для ожидания ответа от вызванного сервиса. После получения ответа тот передает его на обработку новому потоку, который используется для исполнения ветви обработки ответа (Response Pipeline). Данная архитектура подразумевает, что пока осуществляется ожидание ответа от бизнес-сервиса, никакие потоки не используются, что существенно улучшает масштабируемость в терминах использования потоков.
Стоит отметить, что в зависимости от типа транспорта и настроек, поток прокси-сервиса может как блокироваться на вызове бизнес-сервиса, так и не блокироваться - сразу возвращаться в пул потоков, оставляя задачу передачи ответа на обработку muxer'у.
OSB позволяет настроить разные WorkManager'ы для веток обработки запроса и ответа. Нужно понимать, что поток для WorkManager'а бизнес-сервиса используется только для обработки ответа и никогда не используется для обработки запроса.
Использование одного и того же WorkManager'а для прокси- и вызываемого из него бизнес-сервиса приводит к проблемам. Например, при использовании блокирующего вызова с помощью действия Service Callout (см. ниже): в ветки обработки запроса вызывается блокирующий Service Callout, одновременно muxer пытается получить из пула поток для обработки ответа, но не может этого сделать, т.к. все потоки заблокированы на Service Callout'е. При этом продолжают поступать новые запросы и они вынуждены соперничать с ответами за доступные потоки. Решение данной проблемы: убедиться, что для прокси- и бизнес-сервисов используются разные WorkManager'ы.
Если в прокси-сервисе не используется маршрутизация, то сервис работает как эхо: возвращает в качестве ответа переданное в запросе сообщение. При этом в качестве WorkManager'а бизнес-сервиса используется default. Т.е. ветвь обработки ответа в таких прокси-сервисах выполняется с помощью потока для WorkManager'а default.
Если используется вызов прокси-сервиса из прокси-сервиса, то для обработки сообщений используются оригинальные потоки: для ветви обработки запроса - поток первого прокси-сервиса, для ветви обработки ответа - поток бизнес-сервиса.
После того, как в действии Service Callout вызван бизнес-сервис, вызывающий поток блокируется до тех пор, пока не дождется ответа. В отличие от вызова через маршрутизацию, данный поток не освобождается и не возвращается в пул. Muxer при этом использует WorkManager бизнес-сервиса вызванного через Service Callout для размещения потока, обрабатывающего результат вызова. В данном случае под обработкой ответа подразумевается ожидание ответа и уведомление заблокированного потока о его получении. После этого вызвавший действие Service Callout поток продолжает свое исполнение.
Если при этом прокси-сервис, в ветви обработки запроса которого вызывается Service Callout, и бизнес-сервис, вызываемый через данный Service Callout, используют один и тот же WorkManager, то возможно зависание или даже взаимная блокировка потоков. Рассмотрим следующий сценарий:
Решение данной проблемы: убедиться, что все бизнес-сервисы, вызываемые через маршрутизацию и у которых в обработке ответа делается Service Callout, используют иные WorkManager'ы, нежели бизнес-сервисы, вызываемые через данный Callout. Возможны ситуации, когда один и тот же бизнес-сервис вызывается и через маршрутизацию и через Service Callout, в таком случае необходимо сделать его копию и назначить ей другой WorkManager, после этого разделить вызовы: через маршуртизацию вызывать только, например, оригинал, а через Service Callout - вновь созданную копию.
Стоит отметить, что некоторые используемые OSB транспорты являются неблокируемыми, т.е. поддерживают неблокирующий вызов через маршрутизацию. В данном случае поток прокси-сервиса, осуществляющего вызов, не ожидает ответа, а сразу возвращается в пул потоков. Для перехвата результата вызова используется специальный мультиплексор, muxer, который передает его на обработку новому потоку.
Распределение типов транспортов на блокируемые и неблокируемые приведено в таблице:
Является транспорт блокируемым или нет зависит от того, поддерживает ли он передачу контекста транзакции. Например, вызов через EJB всегда осуществляется в контексте транзакции, поэтому неблокируемый режим в данном случае неприемлем. Вызов же через HTTP по-умолчанию осуществляется вне контекста транзакции и поддерживает асинхронный режим с участием muxer'а. Однако, если выставить для маршрутизации значение параметра QoS (качество сервиса) равное Exactly Once, то вызывающий поток будет заблокирован до получения ответа от сервиса, а сам вызов будет сопровождаться передачей контекста транзакции, например по стандарту WS-AtomicTransaction.
В случае двунаправленного вызова через JMS транспорт является неблокируемым в том смысле, что поток, осуществляющий вызов, не будет ждать обработки сообщения, переданного в очередь и формирования ответа на данное сообщение. Работа данного потока заканчивается после записи сообщения в очередь запросов. При этом непосредственно на момент записи поток блокируется.
В заключение хочется сказать, что даже если удастся полностью избежать блокировок на стороне OSB, то производительность рано или поздно упрется в масштабируемость вызываемого сервиса. Наступит момент, когда вызов данного сервиса станет что называется бутылочным горлышком. Однако, стоит заметить, что при использовании действия Service Callout такой момент наступит быстрее, т.к. к проблеме с вызовом медленного сервиса добавится проблема блокировки потоков на стороне самой OSB.
Будьте бдительны, не дайте вашим потокам стать заблокированными!
Рисунки для статьи взяты с сайтов Antony Reynolds' Blog и Techno Babble.
Понравилось сообщение - подпишитесь на блог и Twitter
Общая модель потоков
Общая модель потоков OSB, т.е. модель потоков при использовании маршрутизации, следующая: ветвь обработки запроса выполняется в одном потоке, потоке для WorkManager'а прокси-сервиса, ветвь обработки ответа - в другом потоке - потоке для WorkManager'а бизнес-сервиса. После отправки запроса вызываемому сервису поток прокси-сервиса возвращается в пул потоков. Существует специальный мультиплексор, muxer, который используется для ожидания ответа от вызванного сервиса. После получения ответа тот передает его на обработку новому потоку, который используется для исполнения ветви обработки ответа (Response Pipeline). Данная архитектура подразумевает, что пока осуществляется ожидание ответа от бизнес-сервиса, никакие потоки не используются, что существенно улучшает масштабируемость в терминах использования потоков.
Стоит отметить, что в зависимости от типа транспорта и настроек, поток прокси-сервиса может как блокироваться на вызове бизнес-сервиса, так и не блокироваться - сразу возвращаться в пул потоков, оставляя задачу передачи ответа на обработку muxer'у.
OSB позволяет настроить разные WorkManager'ы для веток обработки запроса и ответа. Нужно понимать, что поток для WorkManager'а бизнес-сервиса используется только для обработки ответа и никогда не используется для обработки запроса.
Использование одного и того же WorkManager'а для прокси- и вызываемого из него бизнес-сервиса приводит к проблемам. Например, при использовании блокирующего вызова с помощью действия Service Callout (см. ниже): в ветки обработки запроса вызывается блокирующий Service Callout, одновременно muxer пытается получить из пула поток для обработки ответа, но не может этого сделать, т.к. все потоки заблокированы на Service Callout'е. При этом продолжают поступать новые запросы и они вынуждены соперничать с ответами за доступные потоки. Решение данной проблемы: убедиться, что для прокси- и бизнес-сервисов используются разные WorkManager'ы.
Модель потоков при отсутствии маршрутизации
Если в прокси-сервисе не используется маршрутизация, то сервис работает как эхо: возвращает в качестве ответа переданное в запросе сообщение. При этом в качестве WorkManager'а бизнес-сервиса используется default. Т.е. ветвь обработки ответа в таких прокси-сервисах выполняется с помощью потока для WorkManager'а default.
Модель потоков при каскадировании прокси-сервисов
Если используется вызов прокси-сервиса из прокси-сервиса, то для обработки сообщений используются оригинальные потоки: для ветви обработки запроса - поток первого прокси-сервиса, для ветви обработки ответа - поток бизнес-сервиса.
Модель потоков при использовании действия Service Callout
После того, как в действии Service Callout вызван бизнес-сервис, вызывающий поток блокируется до тех пор, пока не дождется ответа. В отличие от вызова через маршрутизацию, данный поток не освобождается и не возвращается в пул. Muxer при этом использует WorkManager бизнес-сервиса вызванного через Service Callout для размещения потока, обрабатывающего результат вызова. В данном случае под обработкой ответа подразумевается ожидание ответа и уведомление заблокированного потока о его получении. После этого вызвавший действие Service Callout поток продолжает свое исполнение.
Если при этом прокси-сервис, в ветви обработки запроса которого вызывается Service Callout, и бизнес-сервис, вызываемый через данный Service Callout, используют один и тот же WorkManager, то возможно зависание или даже взаимная блокировка потоков. Рассмотрим следующий сценарий:
- в прокси-сервисе осуществляется вызов действия Service Callout и поток зависает на ожидании ответа;
- множество экземпляров ветви обработки запроса используют один и тот же WorkManager и находятся в том же состоянии, что достаточно характерно для систем, работающих под большой нагрузкой;
- приходит ответ от вызванного сервиса, но все потоки для WorkManager'а размещены для заблокированных экземпляров;
- ответ не может быть обработан действием Service Callout, т.е. не может стартовать поток, который передаст ответ в заблокированный на выполнении данного действия поток с одновременным его разблокированием;
- возникает взаимоблокировка потоков.
Решение данной проблемы: убедиться, что все бизнес-сервисы, вызываемые через маршрутизацию и у которых в обработке ответа делается Service Callout, используют иные WorkManager'ы, нежели бизнес-сервисы, вызываемые через данный Callout. Возможны ситуации, когда один и тот же бизнес-сервис вызывается и через маршрутизацию и через Service Callout, в таком случае необходимо сделать его копию и назначить ей другой WorkManager, после этого разделить вызовы: через маршуртизацию вызывать только, например, оригинал, а через Service Callout - вновь созданную копию.
Блокировки на различных видах транспортов
Стоит отметить, что некоторые используемые OSB транспорты являются неблокируемыми, т.е. поддерживают неблокирующий вызов через маршрутизацию. В данном случае поток прокси-сервиса, осуществляющего вызов, не ожидает ответа, а сразу возвращается в пул потоков. Для перехвата результата вызова используется специальный мультиплексор, muxer, который передает его на обработку новому потоку.
Распределение типов транспортов на блокируемые и неблокируемые приведено в таблице:
Является транспорт блокируемым или нет зависит от того, поддерживает ли он передачу контекста транзакции. Например, вызов через EJB всегда осуществляется в контексте транзакции, поэтому неблокируемый режим в данном случае неприемлем. Вызов же через HTTP по-умолчанию осуществляется вне контекста транзакции и поддерживает асинхронный режим с участием muxer'а. Однако, если выставить для маршрутизации значение параметра QoS (качество сервиса) равное Exactly Once, то вызывающий поток будет заблокирован до получения ответа от сервиса, а сам вызов будет сопровождаться передачей контекста транзакции, например по стандарту WS-AtomicTransaction.
В случае двунаправленного вызова через JMS транспорт является неблокируемым в том смысле, что поток, осуществляющий вызов, не будет ждать обработки сообщения, переданного в очередь и формирования ответа на данное сообщение. Работа данного потока заканчивается после записи сообщения в очередь запросов. При этом непосредственно на момент записи поток блокируется.
Заключение
В заключение хочется сказать, что даже если удастся полностью избежать блокировок на стороне OSB, то производительность рано или поздно упрется в масштабируемость вызываемого сервиса. Наступит момент, когда вызов данного сервиса станет что называется бутылочным горлышком. Однако, стоит заметить, что при использовании действия Service Callout такой момент наступит быстрее, т.к. к проблеме с вызовом медленного сервиса добавится проблема блокировки потоков на стороне самой OSB.
Будьте бдительны, не дайте вашим потокам стать заблокированными!
Ресурсы
- The Threading Model;
- Following the Thread in OSB;
- Oracle Service Bus: thread blocking evidences, transport level.
Рисунки для статьи взяты с сайтов Antony Reynolds' Blog и Techno Babble.
Понравилось сообщение - подпишитесь на блог и Twitter
Комментариев нет:
Отправить комментарий
Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!