As part of implementing a web service it is often necessary to delegate to a number of independent subtasks. For a synchronous service, carrying out these tasks sequentially may take an unacceptable amount of time causing the client to time out waiting on the service. Therefore the preferred approach is to process all independent tasks in parallel and consolidate the results.
This pattern is referred to as a “Split-Join” and comes in two flavours:
This recipe will guide you through a sample implementation of the second example using Oracle Service Bus.
Although it is possible to develop for the Oracle Service Bus using only the Web Console interface, for a fully featured IDE (including support for version control, source code editing plug-ins, lower latency and independence of development environments) it is generally preferable to use the OSB Workshop perspective of the OEPE.
This recipe assumes the use of an existing OSB Configuration project within the OSB Workshop for development so ensure that you have installed and familiarised yourself with it prior to beginning.
Prior to beginning this recipe, you will need to prepare the target WSDL operation which will be invoked to process individual items. In the example, this will be the priceCheck operation of the Book service, which determines how much each should cost.
You should always finalise the service contract prior to beginning development.
If you wish to follow along exactly with these instructions you will require a copy of the schema and WSDL files used in the example. You can obtain a copy of these, as well as a mock implementation of the Book service from the following link: BookStore.zip
A new flow will appear in the main editing window.
Note in the above example the aggregate total has been initialized to 0.
Expression: xs:float($getTotalPriceResponse.parameters/totalPrice) + xs:float($priceCheckResponse.parameters/price)
Before the Split-Join can be used in a proxy service, it must first be encapsulated in a standard OSB Business Service.
The business service is now ready for use in any OSB Proxy Service. Test it out.
Refer to the more completely labelled version of the Split-Join message flow above for an end-to-end, annotated view of the final solution. Procedurally, the pseudo-code for the BookStore example might look to be (just going by the annotations) as follows:
Operation getTotalPrice( book_list ):
totalPrice := 0
for each id in book_list
total_price := total_price + Book.priceCheck( id )
The key difference is that the For Each section has a property called “Parallel” set by default to yes (Note, if desired, this can be set to no to force sequential execution). This instructs Oracle Service Bus to execute all (or as many as it has threads) iterations of the Loop scope within the For Each statement concurrently.
Readers paying close attention will also have noticed that the For Each block does not actually iterate over the book IDs directly; rather the OSB determines the number of Loop scopes simply by counting the number of id nodes and then assigning each scope a different $counter variable integer between 1 and that total count. So a more accurate representation of the pseudo code would be as follows:
Operation getTotalPrice( book_list ):
totalPrice := 0
for counter in 1 .. size(book_list)
total_price := total_price + Book.priceCheck( book_list[counter].id )
Performing this addition in parallel allows the BookStore service to compute the total much faster, dividing the total time of priceChecks by the number of concurrent threads.
The recipe above represents a reasonably standard, cookie-cutter implementation of how one would use the Split-Join feature of Oracle Service Bus to iterate over a dynamic sequence of identical elements in a list. It should be enough to get you started on any similar problem; however it only scratches the surface of the possibilities for what can be accomplished with a Split-Join message flow.
Without appropriate Error Handling logic, the first fault thrown by a service invocation within any one of the Split-Join’s threads will re-raise in the Split-Join and halt the entire message flow.
In order to prevent this, “Catch” clauses need to be added to the scope of each thread as follows:
Rather than simply summing up numerical values, you can aggregate the results of service calls any way you like. A common example is appending the results to a dynamic sequence using an Insert Action.
Note that you are not limited to a single Invoke Service Action. Multiple “child” operations may be invoked sequentially or in parallel.
In fact the premise of a “Static” Split-Join is that instead of using a For Each loop, you would use an explicit Parallel construct (see Flow Control in the Design Palette) and drop a different Invoke Service Action into each lane.
Any combination of flow constructs desired can be layered to create complex concurrent processing systems within a single Split-Join Message Flow.
With any software system involving multi-threading, there is always a possibility of Deadlocks or Conflicts. Although variables within a Split-join message flow are protected from these scenarios, Oracle Service Bus does not provide any built-in mitigation tools for external systems.
It is outside the scope of this discussion to prescribe how one might resolve concurrent update issues in external systems; however designers and developers should always be aware when there is such a possibility and take appropriate action.
|Subscribe to the Rubicon Red Blog|