OSB non blocking HTTP: extreme scalability
Oracle Service Bus provides a non blocking HTTP invoking mechanism, releasing the thread used to make the request until a response is received, so that a new thread is allocated and takes care of the response handling, providing extreme scalability. I aim to show you in this post how it works and provide some theoretical info on why it’s important.
Why do we scale more if we use less threads?
Threads are costly resources and are limited in the sense of how many you can create and still have a responsive system. More threads represent more context switching at the CPU and more work executed by the system, leaving fewer resources to execute what matters: application code. Threads are also expensive to be created, that’s why we generally see pools of them, so releasing a thread quicker will enable you to be able to handle more requests and will, certainly, improve the scalability of your entire system.
What does OSB provide?
When routing to a service which has a non blocking capable transport, depending on the scenario (protocol, type of routing, etc), OSB has the capability to send the request and release the thread until a response is received. This is extremely useful in cases where the response may be too slow so that the released thread may be used to serve other request instead of keeping blocked for a long time. When the callee service has a response to deliver, OSB borrows a thread from WebLogic’s thread pool and uses it to handle the response. OSB has support for a lot of protocols as non blocking and we will explore how this works with HTTP.
Seeing it in practice
Creating a slow service
To see what happens, we first need a slow service, which will allow us to route to it and see the request thread being released back to the pool. This is achieved by the following JAX-WS service which sleeps for 60 Seconds before delivering the response:
package br.com.amadei.osb.nbhttp;
import java.util.Date;
import javax.jws.WebService;
@WebService
public class SlowWS {
public String verySlowOperation() {
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Date().toString();
}
}
Deploy the WAR containing this Web Service to the same WLS running OSB.
We will have another class, which will be called via a Java Callout from OSB to show the current thread. The class is as follows:
package br.com.amadei.osb.nbhttp;
public class LogThread {
public static void logCurrThread() {
System.out.println("***************************** "
+ Thread.currentThread());
}
}
Compile this class and create a JAR. In OSB, create an OSB project and copy this JAR into it. It will be used later.
Now, import the WSDL and XSD of the SlowWS to OSB and create a business service for it as can be seen bellow. This OSB business service should point to the created "SlowWS" JAX-WS web service, deployed as a WAR.
Create a proxy service based on the same WSDL and add a Routing to the created business service. Inside the request and response actions of the Routing, add a java callout to the LogThread.logCurrThread method call. This can be seen bellow:
Create a soapUI project to test the proxy service as testing it via SBCONSOLE won’t release the thread as it will remain active for the web console, so soapUI is a better option in this case.
Before making the first request, go to the WebLogic console. We will take a thread dump to see the state of the thread which served the request. So, after logging in, expand the Environment link and click Servers. Select the AdminServer (supposing you’re running the developer install of OSB), Monitoring, Threads. This is where you’ll be able to take the dump when needed.
Before going back to soapUI to make the request, notice that it will log the thread handling the request, so you will be able to look back at the dump. You should do the following:
- Invoke the request via soapUI;
- Go back to the console and take the name of the thread OSB logged to the system output;
- Take a thread dump and see that this thread is doing;
Let’s try it!
After starting the request via soapUI, I see the following message in the standard output:
***************************** Thread[[ACTIVE] ExecuteThread: '5' for queue: 'weblogic.kernel.Default (self-tuning)',5,Pooled Threads]
This means that the ExecuteThread 5 from weblogic.kernel.Default is handling my request. After that, I took a thread dump and searched for this thread and could see the following stack for it:
[ACTIVE] ExecuteThread: '5' for queue: 'weblogic.kernel.Default (self-tuning)'" waiting for lock weblogic.work.ExecuteThread@42f9ef WAITING
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:485)
weblogic.work.ExecuteThread.waitForRequest(ExecuteThread.java:205)
weblogic.work.ExecuteThread.run(ExecuteThread.java:226)
This stack means this thread is waiting for a request inside WebLogic’s Thread pool or, to be more specific, it was returned to the pool. At this time, the response was not yet to our service and the client is also waiting OSB to deliver the response but, the important thing to notice is that we are not holding a thread until such response available, proving the non blocking capability.
After 1 minute, the response is delivered and we see in the log the thread elected to hold the response (even if the request was blocking, response would have been delivered in a different thread).
The other side
This behavior may seem a little strange at the first seen, so we can see how the behavior would be if this request was blocking. To make it blocking, add a Routing Options node inside the Request Action pipeline and configure its QoS as “Exactly Once” as can be seen in the figure bellow:
The Routing will be blocking if the QoS is exactly once (Service Callout calls are also always blocking).
Deploy the new version and do the same steps done before. This time, Execute Thread 14 was elected to serve my request, and looking at its stack, we see it’s blocked trying to read the response.
[ACTIVE] ExecuteThread: '14' for queue: 'weblogic.kernel.Default (self-tuning)'" RUNNABLE native java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.read(SocketInputStream.java:129) java.io.BufferedInputStream.fill(BufferedInputStream.java:218) java.io.BufferedInputStream.read1(BufferedInputStream.java:258) java.io.BufferedInputStream.read(BufferedInputStream.java:317) weblogic.net.http.MessageHeader.isHTTP(MessageHeader.java:224) weblogic.net.http.MessageHeader.parseHeader(MessageHeader.java:148) weblogic.net.http.HttpClient.parseHTTP(HttpClient.java:468) weblogic.net.http.HttpURLConnection.getInputStream(HttpURLConnection.java:401) weblogic.net.http.SOAPHttpURLConnection.getInputStream(SOAPHttpURLConnection.java:37) weblogic.net.http.HttpURLConnection.getResponseCode(HttpURLConnection.java:1005) com.bea.wli.sb.transports.http.HttpOutboundMessageContext.getResponse(HttpOutboundMessageContext.java:679) com.bea.wli.sb.transports.http.HttpOutboundMessageContext.send(HttpOutboundMessageContext.java:347) com.bea.wli.sb.transports.http.wls.HttpTransportProvider.sendMessageAsync(HttpTransportProvider.java:211)
Here, we saw how the thread could get blocked until a response is received. As we saw during the post, this is worst than the former scenario, so try to leverage non blocking whenever possible - sometimes, however, you have only the option of working with blocking requests.
Conclusion
Oracle Service Bus offers a extremely helpful way for you to scale your system and what’s better is that it requires no coding. Take this into consideration when architecting your services in OSB!













