Dynamic Router¶
This page explains how you can implement a sample scenario of Dynamic Router using WSO2 Micro Integrator.
Introduction to Dynamic Router¶
The Dynamic Router EIP avoids dependence on all possible destinations while maintaining efficiency. It is a router that can self-configure based on special configuration messages from participating destinations. Dynamic Router is available for configuration through a control channel by the receiving parties that can use this control channel.
Sample scenario¶
This sample scenario demonstrates a router that takes an incoming request and decides which back-end service to transmit the message to. To make that decision, it uses a property in the message itself, similar to a Content-Based Router. However, it can also cross-check a registry entry to see if a specific endpoint accepts messages with that property. This approach allows you to reconfigure the router when registry entries change.
The diagram below depicts how to simulate the sample scenario using WSO2 MI.
Before digging into implementation details, let's take a look at the relationship between the sample scenario and the Dynamic Router EIP by comparing their core components.
Dynamic Router EIP | Dynamic Router Sample Scenario |
---|---|
Input Channel | Main sequence |
Dynamic Rule Base | Local Registry |
Dynamic Router | Switch Mediator |
A, B, C | Simple Stock Quote Services |
Synapse configuration of the artifacts¶
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="DynamicRouterProxy" startOnLoad="true" transports="http https" xmlns="http://ws.apache.org/ns/synapse" statistics="disable" trace="disable">
<target>
<inSequence>
<log category="INFO" level="full"/>
<switch source="get-property('To')">
<case regex="http://localhost:9000.*">
<filter xmlns:m0="http://services.samples" xpath="get-property('ConfigA') = //m0:getQuote/m0:request/m0:symbol/text()">
<then>
<call>
<endpoint key="StockQuoteServiceEP1"/>
</call>
</then>
<else>
<log category="INFO" level="custom">
<property name="MESSAGE" value="Registry Value Doesn't Match"/>
</log>
</else>
</filter>
</case>
<case regex="http://localhost:9001.*">
<filter xmlns:m0="http://services.samples" xpath="get-property('ConfigB') = //m0:getQuote/m0:request/m0:symbol/text()">
<then>
<call>
<endpoint key="StockQuoteServiceEP2"/>
</call>
</then>
<else>
<log category="INFO" level="custom">
<property name="MESSAGE" value="Registry Value Doesn't Match"/>
</log>
</else>
</filter>
</case>
<case regex="http://localhost:9002.*">
<filter xmlns:m0="http://services.samples" xpath="get-property('ConfigC') = //m0:getQuote/m0:request/m0:symbol/text()">
<then>
<call>
<endpoint key="StockQuoteServiceEP3"/>
</call>
</then>
<else>
<log category="INFO" level="custom">
<property name="MESSAGE" value="Registry Value Doesn't Matched"/>
</log>
</else>
</filter>
</case>
<default>
<drop/>
</default>
</switch>
<respond/>
</inSequence>
</target>
</proxy>
<?xml version="1.0" encoding="UTF-8"?>
<endpoint name="StockQuoteServiceEP1" xmlns="http://ws.apache.org/ns/synapse">
<address uri="http://localhost:9000/services/SimpleStockQuoteService">
</address>
</endpoint>
<?xml version="1.0" encoding="UTF-8"?>
<endpoint name="StockQuoteServiceEP2" xmlns="http://ws.apache.org/ns/synapse">
<address uri="http://localhost:9001/services/SimpleStockQuoteService">
</address>
</endpoint>
<?xml version="1.0" encoding="UTF-8"?>
<endpoint name="StockQuoteServiceEP3" xmlns="http://ws.apache.org/ns/synapse">
<address uri="http://localhost:9002/services/SimpleStockQuoteService">
</address>
</endpoint>
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="ConfigA" xmlns="http://ws.apache.org/ns/synapse"><![CDATA[foo]]></localEntry>
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="ConfigB" xmlns="http://ws.apache.org/ns/synapse"><![CDATA[bar]]></localEntry>
<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="ConfigC" xmlns="http://ws.apache.org/ns/synapse"><![CDATA[WSO2]]></localEntry>
How the implementation works¶
Let's break down the elements of the configuration:
-
Proxy Service: The main entry point for incoming messages. This proxy service dynamically routes the message to one of three possible endpoints based on the message content and configuration settings.
-
switch: A routing mechanism that examines the
To
header in the SOAP message. Depending on the URL in the header, it determines which case to follow. Each case corresponds to a different backend service. -
case: Each case within the switch block checks if the message is destined for a specific service endpoint (
http://localhost:9000
,http://localhost:9001
, orhttp://localhost:9002
). If the message matches one of these URLs, it proceeds to check a specific property in the message. -
filter: Inside each case, the filter mediator is used to validate the message content. It checks if the symbol in the incoming message matches a pre-configured value stored in the local entry (
ConfigA
,ConfigB
, orConfigC
). If it matches, the message is sent to the corresponding service endpoint. -
local entry: These are registry entries that store the expected values (
foo
,bar
,WSO2
) for each service. They act as the criteria against which incoming messages are validated. -
endpoints: Each of the three endpoints (
StockQuoteServiceEP1
,StockQuoteServiceEP2
,StockQuoteServiceEP3
) corresponds to a different service URL. Depending on the routing logic, the message is sent to one of these endpoints. -
else block: If the message does not match the expected symbol, a log message is generated indicating that the registry value didn't match, and the message is not forwarded to the service.
Set up the sample scenario¶
Follow the below instructions to simulate this sample scenario.
-
Install WSO2 Micro Integrator.
Info
Follow the Install the Micro Integrator Runtime documentation for more information.
-
Launch Visual Studio Code with the Micro Integrator for VS Code extension (MI for VS Code) installed.
Info
Follow the Install Micro Integrator for VS Code documentation for a complete installation guide.
-
Download the backend service.
-
Extract the downloaded zip file.
-
Open a terminal, and navigate to the
axis2Server/bin/
directory inside the extracted folder. -
Execute the following command to start the axis2server with the SimpleStockQuote backend service:
sh axis2server.sh
axis2server.bat
-
Navigate to the
<MI_HOME>/bin/
directory and start thetcpmon
application. -
In the
tcpmon
application, navigate to the Admin tab. Add listeners to ports9001
and9002
, for each listener set the target hostname tolocalhost
and target port to9000
in each instance. -
Download the artifacts of the sample.
-
Import the artifacts to WSO2 MI.
Click File -> Open Folder -> Select the extracted ZIP file to import the downloaded ZIP file.
-
Start the project in the WSO2 MI server.
For instructions, go to Build and Run Documentation.
-
Start SoapUI.
For instructions on downloading and starting, go to SoapUI Getting Started Documentation.
Execute the sample¶
-
Send the following request using SoapUI (or any other Soap client).
POST http://localhost:8290/services/DynamicRouterProxy Accept-Encoding: gzip,deflate Content-Type: text/xml;charset=UTF-8 Connection: Keep-Alive SOAPAction: "urn:getQuote" To: http://localhost:9000/services/SimpleStockQuoteService <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples" xmlns:xsd="http://services.samples/xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing"> <soapenv:Header> <wsa:To>http://localhost:9000/services/SimpleStockQuoteService</wsa:To> <wsa:Action>urn:getQuote</wsa:Action> </soapenv:Header> <soapenv:Body> <ser:getQuote> <ser:request> <ser:symbol>foo</ser:symbol> </ser:request> </ser:getQuote> </soapenv:Body> </soapenv:Envelope>
POST http://localhost:8290/services/DynamicRouterProxy Accept-Encoding: gzip,deflate Content-Type: text/xml;charset=UTF-8 Connection: Keep-Alive SOAPAction: "urn:getQuote" To: http://localhost:9001/services/SimpleStockQuoteService <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples" xmlns:xsd="http://services.samples/xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing"> <soapenv:Header> <wsa:To>http://localhost:9001/services/SimpleStockQuoteService</wsa:To> <wsa:Action>urn:getQuote</wsa:Action> </soapenv:Header> <soapenv:Body> <ser:getQuote> <ser:request> <ser:symbol>bar</ser:symbol> </ser:request> </ser:getQuote> </soapenv:Body> </soapenv:Envelope>
POST http://localhost:8290/services/DynamicRouterProxy Accept-Encoding: gzip,deflate Content-Type: text/xml;charset=UTF-8 Connection: Keep-Alive SOAPAction: "urn:getQuote" To: http://localhost:9002/services/SimpleStockQuoteService <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.samples" xmlns:xsd="http://services.samples/xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing"> <soapenv:Header> <wsa:To>http://localhost:9002/services/SimpleStockQuoteService</wsa:To> <wsa:Action>urn:getQuote</wsa:Action> </soapenv:Header> <soapenv:Body> <ser:getQuote> <ser:request> <ser:symbol>WSO2</ser:symbol> </ser:request> </ser:getQuote> </soapenv:Body> </soapenv:Envelope>
-
Send more requests changing
symbol
to other values.
Analyze the output¶
-
When you send the requests with the
symbol
value set to one offoo
,bar
, orWSO2
, you will receive outputs like the following. Also, you can see intcpmon
that you received a request for the relevant port.<?xml version='1.0' encoding='UTF-8'?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header/> <soapenv:Body> <ns:getQuoteResponse xmlns:ns="http://services.samples"> <ns:return xmlns:ax21="http://services.samples/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax21:GetQuoteResponse"> <ax21:change>3.9782739749100493</ax21:change> <ax21:earnings>12.142627905802117</ax21:earnings> <ax21:high>-142.69159439461524</ax21:high> <ax21:last>145.1810442343239</ax21:last> <ax21:lastTradeTimestamp>Tue Aug 20 11:48:05 IST 2024</ax21:lastTradeTimestamp> <ax21:low>152.09105015765533</ax21:low> <ax21:marketCap>3.707834504517188E7</ax21:marketCap> <ax21:name>foo Company</ax21:name> <ax21:open>151.25901812607216</ax21:open> <ax21:peRatio>-19.743676192563544</ax21:peRatio> <ax21:percentageChange>2.4263061719499506</ax21:percentageChange> <ax21:prevClose>163.9642193925109</ax21:prevClose> <ax21:symbol>foo</ax21:symbol> <ax21:volume>19405</ax21:volume> </ns:return> </ns:getQuoteResponse> </soapenv:Body> </soapenv:Envelope>
-
When you change the
symbol
to a different value, this log will be printed to the WSO2 MI console.INFO {LogMediator} - {proxy:DynamicRouterProxy} MESSAGE = Registry Value Doesn't Matched