Integration service discovery registry
- When the service starts, it will report some of its information, such as the service name, IP, port and other information to the registry. The services communicate with the registry using a mechanism such as a heartbeat, and if the registry and the service are unable to communicate for a long time, the instance will be cancel.When the service goes offline, the registry will delete the instance information.
- The gateway gets service instance information from the registry in near-real time.
- When the user requests the service through the gateway, the gateway selects one instance from the registry for proxy.
It is very easy for APISIX to extend the discovery client, the basic steps are as follows
Add the implementation of registry client in the ‘apisix/discovery/‘ directory;
Implement the function for initialization and the
_M.nodes(service_name)
function for obtaining the list of service instance nodes;If you need the discovery module to export the debugging information online, implement the
_M.dump_data()
function;Convert the registry data into data in APISIX;
Implementation of Eureka client
First, create a directory eureka
under apisix/discovery
;
Then implement the _M.init_worker()
function for initialization and the _M.nodes(service_name)
function for obtaining the list of service instance nodes in init.lua
:
Finally, provide the schema for YAML configuration in the schema.lua
under apisix/discovery/eureka
.
How convert Eureka’s instance data to APISIX’s node?
Here’s an example of Eureka’s data:
{
"applications": {
"application": [
{
"name": "USER-SERVICE", # service name
"instance": [
{
"instanceId": "192.168.1.100:8761",
"hostName": "192.168.1.100",
"app": "USER-SERVICE", # service name
"ipAddr": "192.168.1.100", # IP address
"status": "UP",
"overriddenStatus": "UNKNOWN",
"port": {
"$": 8761,
},
"securePort": {
"$": 443,
"@enabled": "false"
},
"metadata": {
"management.port": "8761",
"weight": 100 # Setting by 'eureka.instance.metadata-map.weight' of the spring boot application
},
"statusPageUrl": "http://192.168.1.100:8761/actuator/info",
"healthCheckUrl": "http://192.168.1.100:8761/actuator/health",
... ...
}
]
}
]
}
}
Deal with the Eureka’s instance data need the following steps :
- select the UP instance. When the value of
overriddenStatus
is “UP” or the value ofoverriddenStatus
is “UNKNOWN” and the value ofstatus
is “UP”. - Host. The
ipAddr
is the IP address of instance; and must be IPv4 or IPv6. - Port. If the value of
port["@enabled"]
is equal to “true”, using the value ofport["\$"]
, If the value ofsecurePort["@enabled"]
is equal to “true”, using the value ofsecurePort["\$"]
. - Weight.
local weight = metadata.weight or local_conf.eureka.weight or 100
The result of this example is as follows:
[
{
"host" : "192.168.1.100",
"port" : 8761,
"weight" : 100,
"metadata" : {
"management.port": "8761"
}
}
]
Add the following configuration to conf/config.yaml
to add different service discovery clients for dynamic selection during use:
This name should be consistent with the file name of the implementation registry in the apisix/discovery/
directory.
Add following configuration in conf/config.yaml
:
eureka:
host: # it's possible to define multiple eureka hosts addresses of the same eureka cluster.
- "http://${username}:${password}@${eureka_host1}:${eureka_port1}"
prefix: "/eureka/"
fetch_interval: 30 # 30s
weight: 100 # default weight for node
timeout:
connect: 2000 # 2000ms
send: 2000 # 2000ms
read: 5000 # 5000ms
Here is an example of routing a request with a URL of “/user/*“ to a service which named “user-service” and use eureka discovery client in the registry :
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"uri": "/user/*",
"upstream": {
"service_name": "USER-SERVICE",
"type": "roundrobin",
"discovery_type": "eureka"
}
}'
HTTP/1.1 201 Created
Date: Sat, 31 Aug 2019 01:17:15 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX web server
{"node":{"value":{"uri":"\/user\/*","upstream": {"service_name": "USER-SERVICE", "type": "roundrobin", "discovery_type": "eureka"}},"createdIndex":61925,"key":"\/apisix\/routes\/1","modifiedIndex":61925},"action":"create"}
Because the upstream interface URL may have conflict, usually in the gateway by prefix to distinguish:
Suppose both A-SERVICE and B-SERVICE provide a /test
API. The above configuration allows access to A-SERVICE’s /test
API through /a/test
and B-SERVICE’s /test
API through /b/test
.
Notice:When configuring upstream.service_name
, upstream.nodes
will no longer take effect, but will be replaced by ‘nodes’ obtained from the registry.
Sometimes we need the discovery client to export online data snapshot in memory when running for debugging, and if you implement the _M. dump_data()
function:
function _M.dump_data()
return {config = local_conf.discovery.eureka, services = applications}
Then you can call its control api as below:
GET /v1/discovery/{discovery_type}/dump