Клиент Джерси без блокировки

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

Обычно Джерси необходимо создать один поток для каждого запроса. И это похоже на тот случай, использую ли я async() (где Jersey создает потоки для меня - я исследовал это в отладчике) или не (где я, очевидно, должен создать темы сам).

Итак, вот одна конкретная ситуация, когда этого недостаточно:

Я отправляю HTTP-сообщения на удаленные серверы со скоростью до 500 запросов в секунду. Но поскольку для получения ответа может потребоваться некоторое время (я рассчитываю до 30 секунд), общее количество потоков может легко достигать нескольких тысяч (в этот момент обычно происходит сбой процесса JVM). Более того, создавать столько тем просто безумие. На самом деле это должно быть проще простого для доступных ресурсов процессора/сети/ОС, чтобы справиться с этой нагрузкой.

Итак, что я хотел бы сделать, так это просто запустить запросы и получить информацию от ОС, когда придет ответ HTTP.

  • Как сказано выше, простое использование target.request(...).async().... не помогает (потому что Джерси просто порождает свои собственные потоки).
  • Кроме того, ограничение количества потоков через new ClientConfig().property(ClientProperties.ASYNC_THREADPOOL_SIZE, 10) вообще не помогает, потому что это означает, что за раз будет отправлено не более 10 запросов, что явно не то, что я хочу (это просто нагромодит очередь).

Я экспериментировал с new ClientConfig().connectorProvider(new GrizzlyConnectorProvider()), чтобы получить поддержку NIO, но вообще не увидел никакой разницы в поведении.

Итак, есть ли способ запустить запрос, не создавая один дополнительный поток для каждого запроса?


person Chris Lercher    schedule 01.10.2014    source источник
comment
Я только что обнаружил, что есть jersey-non-blocking-client и соответствующий сообщение в блоге, где автор обращается к проблеме (хотя он серьезно недооценивает влияние неблокировки на блокировку). Однако, к сожалению, проект для старой версии Джерси, которую я не могу использовать. Может быть, новые версии Джерси уже имеют неблокирующую поддержку? И если да, то как его активировать?   -  person Chris Lercher    schedule 02.10.2014
comment
Количество потоков, которые вы можете запустить, в первую очередь зависит от размера кучи. В 2048 году нет ничего волшебного.   -  person user207421    schedule 03.10.2014
comment
@EJP: Спасибо, я исправил вопрос.   -  person Chris Lercher    schedule 03.10.2014
comment
@EJP: Кстати, ограничение 2048, которое я вижу, похоже, применяется Mac OS X: sysctl kern.num_taskthreads показывает kern.num_taskthreads: 2048 - и кажется, что Oracle JVM фактически использует потоки ядра (интересно).   -  person Chris Lercher    schedule 03.10.2014


Ответы (2)


Я использую CloseableHttpAsyncClient для выполнения асинхронных запросов к внешней службе. он отлично работает с несколькими сотнями запросов в секунду, я не наблюдал такого количества потоков, как в вашем случае. Это внешняя зависимость, которую вы можете интегрировать через Maven через

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpasyncclient</artifactId>
  <version>4.0.1</version>
</dependency>

Надеюсь это поможет.

person baris    schedule 02.10.2014
comment
В конце концов, я выбрал Apache HttpAsyncClient (я также попробовал AsyncHttpClient, как было предложено @Alper, который также отлично работает - только небольшие различия). Насколько я понимаю, текущие версии Jersey не могут обеспечивать неблокирующее поведение, поскольку оно ограничено JAX-RS 2.0 (несколько places упоминают, что это может стать возможным с JAX-RS 3.0) - person Chris Lercher; 04.11.2014

Делайте запросы, используя https://github.com/AsyncHttpClient/async-http-client. , он использует netty. Он будет запускать вызовы из потока запросов и делать обратный вызов вашему коду, поэтому он не связывает потоки запросов контейнера.

person Alper Akture    schedule 01.10.2014
comment
(Я удалил свой предыдущий комментарий, я сделал ошибку) Да, AsyncHttpClient делает это правильно - он создает пул рабочих потоков и может обрабатывать гораздо больше активных запросов, чем рабочие. Тем не менее, было бы довольно много усилий, чтобы переработать наше приложение для использования AsyncHttpClient, поскольку мы используем много функций Джерси... нельзя ли добиться того же с Джерси? - person Chris Lercher; 02.10.2014
comment
Мы используем Джерси для конечных точек нашей службы, а когда нам требуется вызов другой службы, мы используем AsyncHttpClient. Если вы уже используете клиент из трикотажа, думаю, изменить его будет не так просто. Вы можете использовать ExecutorService и future, чтобы сделать вызов из вызывающего потока и выполнить то же самое. Я добавлю еще один пример. - person Alper Akture; 03.10.2014
comment
Использование моего собственного ThreadPool на самом деле не решает проблему: если ThreadPool ограничен, и я использую вызовы синхронизации для Джерси, тогда вызывающие абоненты исполнителя будут заблокированы. Если я использую асинхронные вызовы для Джерси, то Джерси, к сожалению, создает свои собственные потоки (не только рабочие, но и по одному на каждый открытый запрос)... (единственное, что могло бы сработать, это использовать вызовы синхронизации, а затем убить мой поток - но это кажется очень плохой идеей.) - person Chris Lercher; 03.10.2014
comment
Вы правы, это ничего не решит, я удалил этот пример. - person Alper Akture; 03.10.2014
comment
Вы взглянули на это. ? - person Alper Akture; 03.10.2014
comment
Предложение @AlperAkture по использованию асинхронного режима Джерси с AsyncHttpClient — это деньги. Именно то, что я искал. - person bluecollarcoder; 10.06.2015