Я использую Apache Camel для маршрутизации запроса SOAP на основе определенного атрибута в сообщении запроса. Сообщение сопоставляется с регулярным выражением, и если совпадение найдено, запрос будет перенаправлен на «calldestination1», а если нет, то он будет направлен на «calldestination2».
Я использую следующую конфигурацию:
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel = "http://camel.apache.org/schema/spring"
xmlns:cxf = "http://camel.apache.org/schema/cxf"
xmlns:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation = "
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">
<!-- ... -->
<cxf:cxfEndpoint id = "testEndpointTest"
address = "http://localhost:8080/testEndpoint"
endpointName = "s:testEndpoint_Port"
serviceName = "s:testEndpoint"
wsdlURL = "wsdl/testEndpoint.wsdl"
xmlns:s = "http://teste.com/testEndpoint"/>
<!-- ... -->
<camelContext xmlns = "http://camel.apache.org/schema/spring">
<endpoint id = "calldestination1" uri = "http://localhost:8080/destination1?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
<endpoint id = "calldestination2" uri = "http://localhost:8080/destination2?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
<route streamCache = "true">
<!--CXF consumer using MESSAGE format-->
<from uri = "cxf:bean:testEndpointTest?dataFormat=MESSAGE"/>
<choice>
<when>
<simple>${bodyAs(java.lang.String)} regex ${properties:router.regex}</simple>
<to uri = "calldestination1"/>
</when>
<otherwise>
<to uri = "calldestination2"/>
</otherwise>
</choice>
</route>
</camelContext>
Когда целевой сервер, на котором работает «calldestination2», находится под нагрузкой, ответы на запросы могут занимать около 1150 мс. Apache Camel, похоже, не очень хорошо с этим справляется.
Чтобы воспроизвести это поведение, я использовал SoapUI с SOAP MockService с задержкой (скрипт OnRequest) и jmeter.
Сначала я провел тест с SoapUI MockService без задержки, а затем с задержкой 1100 мс.
.
Затем я настроил Apache Camel для маршрутизации запроса в службу SoapUI и повторил тесты.
JMeter -> SoapUI — задержка 0 мс
~1200 запросов в секунду; средний запрос 25 мс; 0% ошибок
JMeter -> SoapUI — задержка 1100 мс
~100 запросов в секунду; средний запрос 1128 мс; 0% ошибок
JMeter -> Apache Camel -> SoapUI — задержка 0 мс
~420 запросов в секунду; средний запрос 285 мс; 0% ошибок
JMeter -> Apache Camel -> SoapUI — задержка 1100 мс
~8 запросов в секунду; среднее время запроса 14800 мс; 97,23% ошибок по тайм-ауту
Время ожидания в Apache Camel установлено на 30 секунд.
Почему в последнем случае у Apache Camel такая низкая производительность и как ее улучшить?
РЕДАКТИРОВАТЬ 1:
Я создал репозиторий на GitHub, который содержит проект Apache Camel, фиктивный сервис SoapUI и тесты jmeter для удобства тестирования.





Основная проблема
Такие проблемы всегда проблема ресурсов. Пока все компоненты имеют достаточно ресурсов и отвечают быстро, все в порядке. Как только один из них сталкивается с ограничением ресурсов, он становится медленным.
В сценарии JMeter-SoapUI преднамеренная задержка SoapUI равен обрабатывается JMeter. Поскольку для ответа SoapUI требуется больше секунды, запросы JMeter в это время остаются открытыми. Если пул потоков JMeter для запросов исчерпан (все потоки ожидают ответа от SoapUI), дальнейшее масштабирование невозможно. По вашим меркам размер пула потоков может быть 100.
Затем вы помещаете верблюда в середину. При этом вы ввести новые пулы потоков. Должен быть один для приема запросов (CXF) и, возможно, один для отправки запросов (Camel HTTP). Теперь задержка SoapUI также должна обрабатываться этими пулами.. Та же ситуация, но теперь пулы потоков компонента Camel являются ограничением.
Предположим, что пул потоков для HTTP-запросов Camel по умолчанию равен 10. JMeter начинает отправлять запросы. Если JMeter отправляет новые запросы быстрее, чем отвечает SoapUI, 10 потоков для отправки HTTP-запросов к SoapUI очень быстро заняты (ожидают SoapUI).
Поступают новые запросы JMeter, но новые HTTP-запросы к SoapUI невозможны до тех пор, пока один из потоков снова не освободится. Около 8 параллельных запросов (по вашим меркам) в этом случае кажутся разумными.
Таким образом, очевидно, что если вы хотите обслуживать 100 запросов в секунду в подобном сценарии, вам необходимо настроить все задействованные пулы потоков для обработки этого. И вы также должны точно настроить различные тайм-ауты (CXF, Camel HTTP).
Ваш код
Один момент, который я заметил в вашем коде, заключается в том, что вы используете Верблюжий HTTP-компонент для своих целевых конечных точек. Этот компонент использует HTTP-клиент Apache 3.x.
Если вы хотите использовать более современный HTTP-клиент Apache, вы должны использовать Верблюжий компонент HTTP4 (4, потому что он использует HTTP-клиент Apache 4.x). Я не знаю, имеет ли это большое значение, но старая версия объявлена «концом жизни» уже много лет.
Другое дело таймауты. Вы пишете, что поставили Camel timeout на 30 секунд. Но это, вероятно, не время ожидания CXF или HTTP-клиента Apache. HTTP-клиенты имеют несколько тайм-аутов: установка соединения может занять слишком много времени, а получение ответа может занять слишком много времени.
Я обновился до HTTP4 и получил явное улучшение производительности. Я могу использовать опцию «connectionsPerRoute» для дальнейшего увеличения параллелизма. Спасибо.