В данной статье производится сравнение работы простейших сервисов реализованных с помощью фреймворка Camel и двух его компонентов: HTTP и AHC. Углубляться в структуру и работу с самим фреймворком не будем, предполагается что читатель уже немного знаком с ним. Рассматривать будем простой сервис на Camel, который получает запросы от jetty-компонента, обрабатывает их, например, выводит в лог, вызывает другой сервис через http, и обрабатывает ответ от него, например, пишет в лог. Использовать будем Camel версии 3.4.0 и Java 8. Для тестирования использовались скрипты JMeter для вызова нашего сервиса в соответствии с задуманной частотой и интервалами, а так же небольшой Http-сервис, играющий роль внешнего по отношению к нашему сервису, и выполняющий задержку 5 секунд. Все взаимодействие происходит по локальной петле (127.0.0.1), так что сетевые задержки не учтены, но для сравнительного анализа они  и не нужны.  В данном разделе будет рассматриваться стандартный HTTP-компонент для взаимодействия по HTTP. Код простого сервиса: Примечание: удаление заголовков, начинающихся на "CamelHttp" необходимо потому, что они выставляются в Jetty-компоненте и могут повлиять на работу Http-компонента. Для проверки работы данного сервиса запустим скрипт JMeter, который отправляет 25 одновременных запросов. Samples Min Max Error % 25 5012 7013 20.000% В результате видим, что 20% или 5 из 25 запросов обработались с ошибкой(Read timed out). Связано это с тем, что у http-компонента по умолчанию установлено ограничение в 20 соединений к одному хосту. Изменяется это ограничение параметром connectionsPerRoute После этого исправления все 25 сообщений обрабатываются без ошибок. Но есть еще одно ограничение – это ограничение пула потоков jetty-компонента, по умолчанию 200.  Для проверки этого ограничения запустим следующие 4 сценария JMeter: 200 одновременных запросов 300 одновременных запросов 300 запросов равномерно распределенных в течении 5 секунд, с повтором 5 раз 200 запросов равномерно распределенных в течении 5 секунд, с повтором 5 раз  После запуска 1 сценария в JVM произошел рост потоков до 214 штук, и далее количество потоков не менялось для всех сценариев.  Результаты выполнения тестовых сценариев:      Процент ошибок 200 запросов единовременно 0% 300 запросов единовременно 34.667% 300 запросов с повтором 5 раз 71.733% 200 запросов с повтором 5 раз 0% Первый и четвертый  сценарии демонстрируют нормальную работу с допустимой нагрузкой  Второй сценарий с 300 одновременными запросами  демонстрирует резкое превышение возможностей настроенного сервиса, 200 запросов обрабатываются потоками jetty-компонента, а остальные 100 дожидаются в пуле задач jetty, и в результате обрабатываются не 5 секунд, а 10. Соответственно 34% ошибок – это примерно эти 100 запросов. Третий сценарий демонстрирует продолжительную работу сервиса по нагрузкой, превышающей его возможности – 300 запросов равномерно распределяются в 5 секундный интервал, и каждый из них повторяется 5 раз, т.е. каждую секунду в сервис поступает 60 запросов, а так как сервис не может обрабатывать более 200 запросов в один момент времени лишние запросы хранятся в пуле задач и для клиента обрабатываются дольше положенных 5 секунд, в результате клиенты отваливаются по таймауту. Четвертый сценарий аналогичен третьему, с тем исключением, что нагрузка допустимая и в сервис не приходит больше запросов, чем он может обработать, очередь задач jetty-компонента пустая. Для того чтобы увеличить количество одновременно обрабатываемых запросов, можно увеличить пул потоков jetty-компонента, однако следует помнить что каждый поток в JVM по умолчанию потребляет 1 МБ ОЗУ для хранения стэка, и бесконечно плодить потоки в современном мире Docker-контейнеров и микросервисов невозможно, лимиты по памяти не позволят это сделать. Лучше рассмотрим другой подход в следующем разделе. AHC-компонент - это еще один компонент фреймворка Camel для взаимодействия по HTTP. Основан он на библиотеке AsyncHttpClient, позволяющей реализовывать асинхронное (реактивное) взаимодействие. За счет этого компонента попытаемся добиться реактивной работы сервиса – в обычном синхронном режиме с http-компонентом наши потоки просто стояли и ждали, пока внешний сервис нам ответ, т.е. в пустую тратили 5 секунд времени. В асинхронном же компоненте они сразу после отправки запроса освобождаются и готовы принимать новые запросы, а ответы на эти запросы при их получении обрабатываются другим пулом потоков. Изменения в нашем сервисе будут совсем небольшие: Сценарий, в котором 300 запросов запускаются единовременно выполнился без ошибок. Что уже плюс, так как синхронный http-компонент не мог его вообще осилить. Рассмотрим состояние потоков JVM: Потоков, если сравнить с предыдущим вариантом тоже сравнительно меньше. Рассмотри результаты других сценариев:    Процент ошибок 300 запросов с повтором 5 раз 0% 800 запросов с повтором 5 раз 0% 1200 запросов   с повтором 5 раз 1.533% 1600 запросов   с повтором 5 раз 15.02% За счет того, что запросы идут не одновременно, общее количество потоков меньше чем в первом сценарии.     В результате можно сделать выводы, что пропускная способность сервиса выросла в несколько раз, ошибки в сценариях с 1200 и 1600 запросов вероятно связаны с задержкой при получении соединений из пула либо задержкой http-заглушки, либо с чем-то еще, но эта тема для другого исследования.    Если в сервисе используется динамическое создание AHC-эндпоинтов, то это может случайно выстрелить вам ногу. Рассмотрим пример: После запуска сценария с единовременным стартом 300 запросов состояние потоков в JVM:  Как видим, поток слишком много.  Дело в том, что по умолчанию AHC-компонент для каждого эндпоинта создает свою инстанцию объекта AsyncHttpClient, у каждой из которых свой пул соединений и потоков, в результате для каждого запроса создается по 2 потока – один поток ввода/вывода, другой поток-таймер для контроля таймаутов и поддержания соединений в состоянии KeepAlive.  Чтобы этого избежать необходимо настроить инстанцию AsyncHttpClient на уровне компонента, которая будет передаваться в эндпоинт при его создании. После этого создание множества инстанций AsyncHttpClient’a прекратятся.