WSO2 API Manager 2.1.0 + is fully capable of supporting dynamic endpoints. As you see in the documentation [1], In order to use the dynamic endpoint, You need to us TO header
The dynamic endpoint sends the message to the address specified in the To header. You can configure dynamic endpoints by setting mediation extensions with a set of conditions to dynamically change the To header.
As you may already understand, with the above implementation, We are setting the to header in the mediation extension as a hardcoded value. If you want to change the endpoint, you need to set the correct mediation extension and re-publish the API.
QUESTION:
How can we change the endpoint dynamically, without re-publishing the API?
- You can read the endpoint from a database
- You can read the endpoint from the file system
- You can use a service registry like Consul
When it comes to production deployment, Calling a DB for each every API request is pretty expensive. What can we do?
Can't we cache the DB response?
Yes, We can. But when the cache is expiring. What if I want to change the endpoint before cache expires?
Same thing with the file system also. Reading from the file for each and every API call is pretty expensive.
Apart from the above, When it comes to distributed deployment in a production environment which multi-data centers, We can not use a database or filesystem until they are shared.
We can overcome all of the above issues with using a multi data center supported service discovery tool which is Consul.
How to use consul with WSO2 API Manager
- Downloaded consul binary from https://www.consul.io/downloads.html
- Added the consul to the $PATH
export CONSUL_HOME=/Users/shammijayasinghe/wso2/tools/consul; export PATH=$PATH:$CONSUL_HOME
- Created a directory for data of consul /Users/shammijayasinghe/wso2/tools/consul/data
- Started the consul with the command consul agent -server -bind=0.0.0.0 -data-dir=/Users/shammijayasinghe/wso2/tools/consul/data1 -bootstrap
Shammis-MacBook-Pro:consul shammijayasinghe$ consul agent -server -bind=0.0.0.0 -data-dir=/Users/shammijayasinghe/wso2/tools/consul/data1 -bootstrap ==> WARNING: Bootstrap mode enabled! Do not enable unless necessary ==> Starting Consul agent... ==> Consul agent running! Version: 'v0.9.2' Node ID: 'a51633be-0115-ccd2-dd25-4d70cf5d6afa' Node name: 'Shammis-MacBook-Pro.local' Datacenter: 'dc1' Server: true (bootstrap: true) Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600) Cluster Addr: 10.0.0.3 (LAN: 8301, WAN: 8302) Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false ==> Log data will now stream in as it occurs: 2017/09/01 08:32:46 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:10.0.0.3:8300 Address:10.0.0.3:8300}] 2017/09/01 08:32:46 [INFO] raft: Node at 10.0.0.3:8300 [Follower] entering Follower state (Leader: "") 2017/09/01 08:32:46 [INFO] serf: EventMemberJoin: Shammis-MacBook-Pro.local.dc1 10.0.0.3 2017/09/01 08:32:46 [WARN] serf: Failed to re-join any previously known node 2017/09/01 08:32:46 [INFO] serf: EventMemberJoin: Shammis-MacBook-Pro.local 10.0.0.3 2017/09/01 08:32:46 [WARN] serf: Failed to re-join any previously known node 2017/09/01 08:32:46 [INFO] consul: Handled member-join event for server "Shammis-MacBook-Pro.local.dc1" in area "wan" 2017/09/01 08:32:46 [INFO] consul: Adding LAN server Shammis-MacBook-Pro.local (Addr: tcp/10.0.0.3:8300) (DC: dc1) 2017/09/01 08:32:46 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp) 2017/09/01 08:32:46 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp) 2017/09/01 08:32:46 [INFO] agent: Started HTTP server on 127.0.0.1:8500 2017/09/01 08:32:52 [WARN] raft: Heartbeat timeout from "" reached, starting election 2017/09/01 08:32:52 [INFO] raft: Node at 10.0.0.3:8300 [Candidate] entering Candidate state in term 8 2017/09/01 08:32:52 [INFO] raft: Election won. Tally: 1 2017/09/01 08:32:52 [INFO] raft: Node at 10.0.0.3:8300 [Leader] entering Leader state 2017/09/01 08:32:52 [INFO] consul: cluster leadership acquired 2017/09/01 08:32:52 [INFO] consul: New leader elected: Shammis-MacBook-Pro.local 2017/09/01 08:32:52 [INFO] agent: Synced node info
Now the consul server is up and running on my local machine. Now what we need to do is we need to add a key-value pair to the consul registry.
Insert a Key-Value pair using API call
curl --request PUT --data "http://www.mocky.io/v2/59a96c49100000300d3e0afa" http://127.0.0.1:8500/v1/kv/MyMockEndpoint
check and verify whether the key-value pair is available in consul
curl http://127.0.0.1:8500/v1/kv/MyMockEndpoint [{"LockIndex":0,"Key":"MyMockEndpoint","Flags":0,"Value":"aHR0cDovL3d3dy5tb2NreS5pby92Mi81OWE5NmM0OTEwMDAwMDMwMGQzZTBhZmE=","CreateIndex":8,"ModifyIndex":183}]
Now it is verified that with the API call, We can retrieve the value we stored.
Next step is to use this within wso2 API Manager. In order to do that, I have created a mediation extension with the following synapse configuration.
<?xml version="1.0" encoding="UTF-8"?>
<sequence name="customRoutingSequence" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<log>
<property name="State" value="Inside Custom Routing Sequence"/>
</log>
<call blocking="true">
<endpoint>
<address uri="http://127.0.0.1:8500/v1/kv/MyMockEndpoint"/>
</endpoint>
</call>
<log level="full">
<property name="Response From Consul" value="======"/>
</log>
<property description="consulEndpoint" expression="json-eval($.[0].Value)" name="consulEndpoint" scope="default" type="STRING"/>
<property description="decodedConsulEndpoint" expression="base64Decode($ctx:consulEndpoint)" name="decodedConsulEndpoint" scope="default" type="STRING"/>
<log>
<property expression="$ctx:decodedConsulEndpoint" name="decodedConsulEndpoint"/>
</log>
<header name="To" expression="$ctx:decodedConsulEndpoint"/>
</sequence>
I am decoding it with the following property mediator.
<property description="decodedConsulEndpoint" expression="base64Decode($ctx:consulEndpoint)" name="decodedConsulEndpoint" scope="default" type="STRING"/>
Then I am setting it to the TO header as we discussed in the beginning.
<header name="To" expression="$ctx:decodedConsulEndpoint"/>
Then we have to create an API with using
- Above created mediation extension
- Using the endpoint type as Dynamic
Once deployed, Invoke the API.
Then in order to check the dynamic endpoint change, Change the value of the above key by following curl command.
curl --request PUT --data "[newEndpointURL]" http://127.0.0.1:8500/v1/kv/MyMockEndpoint
Once you provide the new endpoint URL above, You should be able to see that API is invoking this given backend URL without republishing the API.
With this way, We can make the API Manager really flexible in a distributed environment, As consul can be configured as a cluster and supported with Multi Data Centers. So, You can get your requiement of changing the backend endpoint of a cluster of API Manager which contains any number of nodes with a single curl command.
[1] https://docs.wso2.com/display/AM210/Working+with+Endpoints