Apache Camel Route performance degradation under load - soap

I'm using Apache Camel to route a SOAP request based on a certain attribute in the request message. The message is matched against a regex and if a match is found the request will be routed to "calldestination1" and if not, it will be routed to "calldestination2".
I'm using the following configuration:
<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>
When the destination server, where "calldestination2" runs, is under load the requests can take around 1150ms to respond. Apache Camel does not seem to handle this very well.
To replicate this behavior I used SoapUI with a SOAP MockService with a delay (OnRequest Script) and jmeter.
Fist I ran the test against SoapUI MockService with no delay and then with a 1100ms delay.
Then I configured Apache Camel to route the request to SoapUI service and repeated the tests.
JMeter -> SoapUI - 0ms delay
~1200 requests per second; 25ms request average; 0% Errors
JMeter -> SoapUI - 1100ms delay
~100 requests per second; 1128ms request average; 0% Errors
JMeter -> Apache Camel -> SoapUI - 0ms delay
~420 requests per second; 285ms request average; 0% Errors
JMeter -> Apache Camel -> SoapUI - 1100ms delay
~8 requests per second; 14800ms request average; 97.23% Errors by timeout
The timeout in Apache Camel is set to 30 seconds.
Why is Apache Camel having such a low performance in the last case and how can I improve the it?
EDIT 1:
I created a repository in GitHub that contains the Apache Camel project, SoapUI mock service and jmeter tests for easy testing.
https://github.com/jraimundo/apache-camel-route-tester

Basic problem
Such problems are always a problem of resources. As long as all components have enough resources and answer fast, all is fine. As soon as one of them encounters a resource limitation it becomes slow.
In the JMeter-SoapUI scenario, the intentional latency of SoapUI is handled by JMeter. Because SoapUI takes more than a second to respond, the JMeter requests are held open for this time. If the JMeter thread pool for requests is exhausted (all threads are waiting for an answer from SoapUI), it cannot further scale. Based on your measures, the thread pool size could be 100.
Then you put Camel in the middle. With this you introduce new thread pools. There must be one to receive requests (CXF) and probably one that sends requests (Camel HTTP). Now the SoapUI latency must also be handled by these pools. Same situation, but now the thread pools of the Camel component are the limitation.
Let's assume the thread pool for Camel HTTP requests is 10 by default. JMeter begins to send requests. If JMeter sends new requests faster than SoapUI responds, the 10 threads to send HTTP requests to SoapUI are very fast all busy (waiting for SoapUI).
New requests of JMeter arrive, but no new HTTP requests to SoapUI are possible until until one of the threads is free again. Around 8 parallel requests (from your measures) seems to be reasonable in this case.
So it is obvious that if you want to serve 100 requests per second in a scenario like this you need to tune all involved thread pools to handle this. And you must also fine tune the different timeouts (CXF, Camel HTTP).
Your code
One point I noticed in your code is that you use the Camel HTTP component for your target endpoints. That component uses the Apache HTTP client 3.x.
If you want to use a more current Apache HTTP client, you must use the Camel HTTP4 component (4 because it uses Apache HTTP client 4.x). I don't know if it makes a big difference, but the old version is declared as "end of life" since years.
Another thing are the timeouts. You write that you set the Camel timeout to 30 seconds. But that is probably not the timeout of CXF or the Apache HTTP client. HTTP clients have multiple timeouts: it can take too long to establish a connection and it can take too long to receive a response.

Related

How to implement openIdConnect on a Camel-REST consumer

I have an OAS specification requiring openIdConnect as a securityScheme on all operations. The consumer will be deployed on in an OSGI container (Karaf 4.4.1). The camel version used is 3.18.4 (but could be moved higher will require some work but doable).
The rest consumer is described using blueprint:
<restConfiguration component="netty-http" port="{{LISTEN_PORT}}" host="{{LISTEN_HOST}}" apiComponent="rest-api">
</restConfiguration>
<rest path="/base_path" consumes="application/json" produces="application/json">
<securityDefinitions>
<openIdConnect key="openIdConnect" description="OpenIdConnect via KeyCloak" url="http://localhost:8080/realms/publishvisit-entry/.well-known/openid-configuration"/>
</securityDefinitions>
<securityRequirements key="openIdConnect"/>
<post path="/entities" >
<param name="body" type="body" required="true"/>
<security key="openIdConnect"/>
<to uri="direct:handle-entities" />
</post>
</rest>
Just to keep things simple (all components are run a local development system not using https yet for the openId discovery (and KeyCloak is running in dev mode).
The problem with this config is wether I have a token (which would make sense) or not (the problem) the route is executed. I would expect an error to be returned when no token is send in the request or an expired a token is send.
It seems no validate on the security is being done.
The Rest component is supposed to be used for both consumer as well producers, I am (for now) only interested in the consumer part.

Error while using authorization in SoapUI

I am trying to test my SOAP webservice running on weblogic 12c (which is not installed locally) using SoapUI as a client. Without authorization everything works fine, but when I implement very simple UserToken on server and do every step described here:
https://www.soapui.org/soap-and-wsdl/authenticating-soap-requests.html
then I've got following error:
<faultcode>wsse:InvalidSecurityToken</faultcode>
<faultstring>Security token failed to validate. weblogic.xml.crypto.wss.SecurityTokenValidateResult#211ca945[status: false][msg UNT Error:Message Created time past the current time even accounting for set clock skew]</faultstring>
Also I've looked to the http log of SoapUI and I've noticed something strange there:
Tue Mar 20 09:15:52 CET 2018:DEBUG:>>
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="..." xmlns:wsu="...">
<wsse:UsernameToken wsu:Id="UsernameToken-374B6AAD9B07D377D515215337525091">
<wsse:Username>user</wsse:Username>
<wsse:Password Type="...#PasswordText">password</wsse:Password>
<wsse:Nonce EncodingType="...#Base64Binary">urRvoAYbjjovfD0OQqvJ6g==</wsse:Nonce>
<wsu:Created>2018-03-20T08:15:52.464Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
The time of log (which is current one) is different then in <wsu:Created> tag. I don't know if it is important, but I am in UTC+01:00 time zone.
EDIT
I've give up with SoapUI and I've implemented my own Java client with authorization using CredentialProvider:
((BindingProvider) port).getRequestContext().put(WSSecurityContext.CREDENTIAL_PROVIDER_LIST, credentialProviders);
And then it was the same! So probably it isn't SoapUI problem, but something else.
Exception in thread "main" com.sun.xml.ws.fault.ServerSOAPFaultException:
Client received SOAP Fault from server: Security token failed to validate. weblogic.xml.crypto.wss.SecurityTokenValidateResult#37b70a09[status: false]
[msg UNT Error:Message Created time past the current time even accounting for set clock skew] Please see the server log to find more detail regarding exact cause of the failure.
at com.sun.xml.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:193)
at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:131)
at com.sun.xml.ws.client.sei.StubHandler.readResponse(StubHandler.java:253)
at com.sun.xml.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:203)
at com.sun.xml.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:290)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:92)
...
When I've looked to the message using Wireshark, time in header was also decrease by one hour. That is really strange and I have no idea what is going on. How am I supposed to test my webservice using authorization? Locally I am using Windows 7, but my Weblogic is running on CentOS 7. As I said both are set to UTC+01:00.
OK, I finally menage to solve this issue. It turns out that server time is late for 2 minutes comparing to the local machine time. Webservice requires time between client and server cannot be larger then few seconds. When I adjusted time everything is working properly.
Additionally, current time in log Mar 20 09:15:52 CET 2018 and time in message 2018-03-20T08:15:52.464Z are equal, but just in different format.

JMeter changing my namespace

I'm trying to use JMeter to invoke a RPC/SOAP Web service and when I invoke the service my namespaces are mangled from the actual values to NS1.
<?xml version="1.0" encoding="UTF-8"?>
<wpc:invoke xmlns:wpc="http://wpc.ibm.com">
<wpc:envelope communicationVersion="5.3">
<wpc:WPCResponseType>asynchronous</wpc:WPCResponseType>
<wpc:wpcHeader>
<wpc:companyName>mycompany</wpc:companyName>
<wpc:wpsUserID>me</wpc:wpsUserID>
<wpc:wpcUserID>wpcUsername</wpc:wpcUserID>
<wpc:password />
<wpc:messageIdentifier>9E2FA100-BE54-11E5-8A91-BF48E24665E0</wpc:messageIdentifier>
<wpc:timestamp>2016-01-18</wpc:timestamp>
<wpc:supplierId><![CDATA[0Z188]]></wpc:supplierId>
<wpc:localeForDisplay>en_US</wpc:localeForDisplay>
<wpc:localeRestriction>en_US</wpc:localeRestriction>
</wpc:wpcHeader>
<wpc:wpcBody>
<wpc:wpcCommand mode="ASYNC" type="UPLOAD">
<wpc:wpcCatalogName>Item Transaction Catalog</wpc:wpcCatalogName>
<wpc:wpcFileDocStorePath>test_data/upload/0003_items.csv</wpc:wpcFileDocStorePath>
<wpc:wpcUpdateOnly>false</wpc:wpcUpdateOnly>
</wpc:wpcCommand>
</wpc:wpcBody>
</wpc:envelope>
</wpc:invoke>
Changes to:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:invoke xmlns:ns1="http://wpc.ibm.com">
<ns1:envelope communicationVersion="5.3">
<ns1:WPCResponseType>asynchronous</ns1:WPCResponseType>
<ns1:wpcHeader>
<ns1:companyName>mycompany</ns1:companyName>
<ns1:wpsUserID>me</ns1:wpsUserID>
<ns1:wpcUserID>wpcUsername</ns1:wpcUserID>
<ns1:password/>
<ns1:messageIdentifier>9E2FA100-BE54-11E5-8A91-BF48E24665E0</ns1:messageIdentifier>
<ns1:timestamp>2016-01-18</ns1:timestamp>
<ns1:supplierId><![CDATA[0Z188]]></ns1:supplierId>
<ns1:localeForDisplay>en_US</ns1:localeForDisplay>
<ns1:localeRestriction>en_US</ns1:localeRestriction>
</ns1:wpcHeader>
<ns1:wpcBody>
<ns1:wpcCommand mode="ASYNC" type="UPLOAD">
<ns1:wpcCatalogName>Item Transaction Catalog</ns1:wpcCatalogName>
<ns1:wpcFileDocStorePath>test_data/upload/0003_items.csv</ns1:wpcFileDocStorePath>
<ns1:wpcUpdateOnly>false</ns1:wpcUpdateOnly>
</ns1:wpcCommand>
</ns1:wpcBody>
</ns1:envelope>
</ns1:invoke>
There must be a setting in JMeter to keep the message from being transformed from my original meaningful namespace to this arbitrary namespace called NS1? When the message is received at the target endpoint it cannot parse the request because of this semantic error.
Any/all replies are appreciated!
MG
JMeter should not change anything in the request body, maybe it is an issues with your web service? Double check the request which is being sent by JMeter using a sniffer tool like Wireshark
In any case try switching to HTTP Request sampler, this is recommended way of sending web service requests (just don't forget to add HTTP Header Manager to send Content-Type and SOAPAction headers).
References:
JMeter User's Manual: Building a SOAP WebService Test Plan
Testing SOAP/REST Web Services Using JMeter

Understanding HTTP 504 Result from Spray Web Service

Given:
client <-- HTTP --> spray web service <-- HTTP --> other web service
The client receives the following HTTP status code and entity body when hitting the spray web service:
504 Gateway Timeout - Empty
Per this answer, my understanding is that the spray web service is not receiving a timely response from the other web service, and thus sending an HTTP 504 to the client.
Assuming that's reasonable, per https://github.com/spray/spray/blob/master/spray-http/src/main/scala/spray/http/StatusCode.scala#L158, I'm guessing that one of these server config values is responsible for the 504 in the spray web service's HTTP response to the client.
What config or implicit would cause the spray web service to reply with a 504 to the client?
I think you are using the default Spray timeouts and perhaps you will need to increase them. If this is the case, there are 2 values you will have to configure to increase the timeouts.
idle-timeout: Time you can have an idle connection to your server before it is disconnected (default 60s).
request-timeout: Time a request from your client (from your server to another) can be idle before it timesout (default 20s).
The first value must be always higher than the second, as the idle-timeout will make pointless the connections from your request client.
So just overwrite your configuration in your application.conf like this:
spray.can {
server {
idle-timeout = 120 s
request-timeout = 20 s
}
}

Spray.can.server.request-timeout property has no effect

In my src/main/resources/application.conf I include:
spray.can.server {
request-timeout = 1s
}
In order to test this, in the Future which is servicing my request I put a Thread.sleep(10000).
When I issue a request, the server waits 10 seconds and responds with no hint of a timeout being sent to the client.
I am not overriding the timeout handler.
Why are my clients (chrome and curl) not receiving a timeout?
The configuration looks correct, so Spray request timeout should be working. One of the frequent reasons for it not working is that your config application.conf is not being used by the application.
The reasons for config being ignored could be that it's in the wrong place, not included in your classpath, or not included in a JAR that you package.
To troubleshoot first check that default Spray timeout is working. By default it's 20 sec. Make your code sleep for 30sec and see if you get timeouts triggered.
Check what's in your final config values by printing it. Set this in your conf:
akka {
# Log the complete configuration at INFO level when the actor system is started.
# This is useful when you are uncertain of what configuration is used.
log-config-on-start = on
}
Finally, keep in mind other timeouts like timeout-timeout = 2 s.
I think the request-timeout is for the http client, if the response is not returned before that value, the client will get a timeout from spray, see the spary doc
# If a request hasn't been responded to after the time period set here
# a `spray.http.Timedout` message will be sent to the timeout handler.
# Set to `infinite` to completely disable request timeouts.
For example, in my web browser, I can got a below message:
Ooops! The server was not able to produce a timely response to your request.
Please try again in a short while!
If the timeout is long enough, the browser will keep waiting until a response is returned