public void request(String url, @Connection HttpClient client, @Content String body, (1)
CompletionCallback<InputStream, HttpAttributes> callback ) { (2)
client.send(url, body, new HttpResponseCallback() {
void onResponse(HttpResponse response) {
callback.success(Result.<InputStream, HttpAttributes>builder() (3)
.output(response.getBody())
.attributes(toAttributes(response))
.build());
}
void onError(Exception e) {
callback.error(e); (4)
}
});
}
Non-Blocking Operations
The Mule 4 execution engine is based on reactive streams. That means there’s top level support for non-blocking operations. By default, all operations are blocking. As you probably noticed, the semantics of all the operation examples you have seen before are inherently blocking: the runtime invokes a method and gets a value in response. That is fine for operations that are more CPU intensive than they are IO intensive, or for operations that consume APIs that cannot be consumed in a non-blocking way, such as JDBC (the API that powers the Database connector).
HTTP is one of the protocols that does supports this. Being able to perform non-blocking HTTP request is key for scalability of gateway proxies. Any connector consuming a REST API can also benefit from this.
This simplified HTTP request example shows how the SDK allows you to develop non-blocking operations:
1 | Unlike blocking operations, the return type is not specified through the method’s return type. Non-blocking operations always need to be specified through void methods. |
2 | An operation becomes non-blocking when it has an argument of type CompletionCallback . This interface uses generics to
specify the operation’s return type. The first generic is the type of the output payload, and the second one the type of the
output attributes. These generics are mandatory. |
3 | The return value is passed through the success(Result) method of the CompletionCallback . See Result for more
information on the Result object |
4 | Non-blocking operations should not throw exceptions. Any errors should be channeled through the error() method in the callback. |
Notice that in this example, the CompletionCallback is being consumed from between an HttpResponseCallback . This second callback
is provided by an HttpClient that supports asynchronous responses. As stated previously, in order to do non-blocking, you need to be consuming an API which allows that.
|
The result of this code is a non-blocking operation that returns an InputStream
as payload and an HttpAttributes
object as message attributes.
Void Operations
It is possible to have a void non-blocking operation. The use case for that is an IO operation that does not return anything, such as writing to a file:
public void write(String path, @Content byte[] bytes, CompletionCallback<Void, Void> callback) {
writeAsync(result -> callback.success());
}
As you can see here, you can use Java’s Void
type to indicate that the operation is void.
Execution Types
As stated above, Mule 4 now has a reactive execution engine. Unlike Mule 3.x, where each flow had its own thread pool, SEDA queues, and so on, the Mule 4 runtime has a few global executors through which all tasks are run.
To schedule tasks correctly, the runtime needs to know which kind of processing each operation is going to perform. The different processing types are:
-
CPU_INTENSIVE: Intensive processing, such as a complex, time-consuming calculations or transformations. Note that the SDK will never infer a
CPU_INTENSIVE
type. See Specifying Execution Types. -
CPU_LITE: Processing that neither blocks nor is CPU intensive, such as message passing, filtering, routing, or non-blocking IO.
-
BLOCKING: Blocking processing that performs blocking IO operations,
Thread.sleep(long)
,Lock.lock()
, or any other technique that blocks the current thread during processing. Although this type includesThread.sleep
, using it is a bad practice that you should avoid.
Inferring Execution Types Automatically
The SDK follows these rules to make a best guess about the execution type so that you don’t have to specify it explicitly:
-
Operation requires connection and is blocking:
BLOCKING
-
Operation requires connection and is non-blocking:
CPU_LITE
-
None of the above:
CPU_LITE
If the execution type to which your operation corresponds does not match the best guess, you should specify the correct one. Failing to do so will negatively impact the performance of any app that uses your module.