This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 8757080 feat: add nacos support (#3820)
8757080 is described below
commit 8757080697d04071ee17e0d43417b9649b8e2b48
Author: _chg_ <[email protected]>
AuthorDate: Wed Apr 21 17:48:56 2021 +0800
feat: add nacos support (#3820)
Co-authored-by: spacewander <[email protected]>
---
.github/workflows/centos7-ci.yml | 31 ++++
README.md | 2 +-
apisix/discovery/nacos.lua | 329 ++++++++++++++++++++++++++++++++++++
ci/linux_openresty_common_runner.sh | 31 ++++
ci/linux_tengine_runner.sh | 31 ++++
docs/en/latest/config.json | 1 +
docs/en/latest/discovery.md | 4 +-
docs/en/latest/discovery/nacos.md | 101 +++++++++++
docs/zh/latest/README.md | 2 +-
docs/zh/latest/discovery.md | 2 +
t/discovery/nacos.t | 253 +++++++++++++++++++++++++++
11 files changed, 784 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/centos7-ci.yml b/.github/workflows/centos7-ci.yml
index 17c0bc9..0d1b899 100644
--- a/.github/workflows/centos7-ci.yml
+++ b/.github/workflows/centos7-ci.yml
@@ -101,6 +101,37 @@ jobs:
docker run --rm --name consul_1 -d -p 8500:8500 consul:1.7 consul
agent -server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
docker run --rm --name consul_2 -d -p 8600:8500 consul:1.7 consul
agent -server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
+ # start nacos server
+ nohup docker network rm nacos_net > /dev/null 2>&1 &
+ nohup docker network create nacos_net > /dev/null 2>&1 &
+ # nacos no auth server - for test no auth
+ docker run --rm -d --name nacos_no_auth --network nacos_net --hostname
nacos2 --env NACOS_SERVERS="nacos1:8848 nacos2:8848" --env
PREFER_HOST_MODE=hostname --env MODE=cluster --env EMBEDDED_STORAGE=embedded
--env JVM_XMS=512m --env JVM_XMX=512m --env JVM_XMN=256m -p8858:8848
nacos/nacos-server:1.4.1
+ # nacos auth server - for test auth
+ docker run --rm -d --name nacos_auth --network nacos_net --hostname
nacos1 --env NACOS_AUTH_ENABLE=true --env NACOS_SERVERS="nacos1:8848
nacos2:8848" --env PREFER_HOST_MODE=hostname --env MODE=cluster --env
EMBEDDED_STORAGE=embedded --env JVM_XMS=512m --env JVM_XMX=512m --env
JVM_XMN=256m -p8848:8848 nacos/nacos-server:1.4.1
+ url="127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200"
]]; do
+ echo 'wait nacos server...'
+ sleep 1;
+ done
+ # register nacos service
+ rm -rf tmp
+ mkdir tmp
+ cd tmp
+ wget
https://raw.githubusercontent.com/api7/nacos-test-service/main/spring-nacos-1.0-SNAPSHOT.jar
+ curl
https://raw.githubusercontent.com/api7/nacos-test-service/main/Dockerfile |
docker build -t nacos-test-service:1.0-SNAPSHOT -f - .
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=1 -p 18001:18001 --name
nacos-service1 nacos-test-service:1.0-SNAPSHOT
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=2 -p 18002:18001 --name
nacos-service2 nacos-test-service:1.0-SNAPSHOT
+ url="127.0.0.1:18002/hello"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200"
]]; do
+ echo 'wait nacos service...'
+ sleep 1;
+ done
+ until [[ $(curl -s
"127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2" | grep
"APISIX-NACOS") ]]; do
+ echo 'wait nacos reg...'
+ sleep 1;
+ done
+ cd ..
+
- name: Install dependencies
run: |
docker exec centos7Instance bash -c "cd apisix && ./ci/centos7-ci.sh
install_dependencies"
diff --git a/README.md b/README.md
index f2e94f9..8272281 100644
--- a/README.md
+++ b/README.md
@@ -117,7 +117,7 @@ A/B testing, canary release, blue-green deployment, limit
rate, defense against
- **OPS friendly**
- OpenTracing: support [Apache
Skywalking](docs/en/latest/plugins/skywalking.md) and
[Zipkin](docs/en/latest/plugins/zipkin.md)
- - works with external service discovery:In addition to the built-in etcd, it
also supports `Consul` and `Nacos` [DNS discovery
mode](https://github.com/apache/apisix/issues/1731#issuecomment-646392129), and
[Eureka](docs/en/latest/discovery.md)
+ - works with external service discovery:In addition to the built-in etcd, it
also supports [Consul](docs/en/latest/discovery/consul_kv.md) and
[Nacos](docs/en/latest/discovery/nacos.md), and
[Eureka](docs/en/latest/discovery.md)
- Monitoring And Metrics: [Prometheus](docs/en/latest/plugins/prometheus.md)
- Clustering: APISIX nodes are stateless, creates clustering of the
configuration center, please refer to [etcd Clustering
Guide](https://etcd.io/docs/v3.4.0/op-guide/clustering/).
- High availability: Support to configure multiple etcd addresses in the
same cluster.
diff --git a/apisix/discovery/nacos.lua b/apisix/discovery/nacos.lua
new file mode 100644
index 0000000..cbb0bb6
--- /dev/null
+++ b/apisix/discovery/nacos.lua
@@ -0,0 +1,329 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local require = require
+local local_conf = require('apisix.core.config_local').local_conf()
+local http = require('resty.http')
+local core = require('apisix.core')
+local ipairs = ipairs
+local type = type
+local math = math
+local math_random = math.random
+local error = error
+local ngx = ngx
+local ngx_re = require('ngx.re')
+local ngx_timer_at = ngx.timer.at
+local ngx_timer_every = ngx.timer.every
+local string = string
+local string_sub = string.sub
+local str_byte = string.byte
+local str_find = core.string.find
+local log = core.log
+
+local default_weight
+local applications
+local auth_path = 'auth/login'
+local instance_list_path = 'ns/instance/list?healthyOnly=true&serviceName='
+
+local host_pattern = [[^http(s)?:\/\/[a-zA-Z0-9-_.:\@]+$]]
+local prefix_pattern = [[^[\/a-zA-Z0-9-_.]+$]]
+local schema = {
+ type = 'object',
+ properties = {
+ host = {
+ type = 'array',
+ minItems = 1,
+ items = {
+ type = 'string',
+ pattern = host_pattern,
+ minLength = 2,
+ maxLength = 100,
+ },
+ },
+ fetch_interval = {type = 'integer', minimum = 1, default = 30},
+ prefix = {
+ type = 'string',
+ pattern = prefix_pattern,
+ maxLength = 100,
+ default = '/nacos/v1/'
+ },
+ weight = {type = 'integer', minimum = 1, default = 100},
+ timeout = {
+ type = 'object',
+ properties = {
+ connect = {type = 'integer', minimum = 1, default = 2000},
+ send = {type = 'integer', minimum = 1, default = 2000},
+ read = {type = 'integer', minimum = 1, default = 5000},
+ },
+ default = {
+ connect = 2000,
+ send = 2000,
+ read = 5000,
+ }
+ },
+ },
+ required = {'host'}
+}
+
+
+local _M = {}
+
+
+local function request(request_uri, path, body, method, basic_auth)
+ local url = request_uri .. path
+ log.info('request url:', url)
+ local headers = {}
+ headers['Accept'] = 'application/json'
+
+ if basic_auth then
+ headers['Authorization'] = basic_auth
+ end
+
+ if body and 'table' == type(body) then
+ local err
+ body, err = core.json.encode(body)
+ if not body then
+ return nil, 'invalid body : ' .. err
+ end
+ headers['Content-Type'] = 'application/json'
+ end
+
+ local httpc = http.new()
+ local timeout = local_conf.discovery.nacos.timeout
+ local connect_timeout = timeout.connect
+ local send_timeout = timeout.send
+ local read_timeout = timeout.read
+ log.info('connect_timeout:', connect_timeout, ', send_timeout:',
send_timeout,
+ ', read_timeout:', read_timeout)
+ httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)
+ local res, err = httpc:request_uri(url, {
+ method = method,
+ headers = headers,
+ body = body,
+ ssl_verify = true,
+ })
+ if not res then
+ return nil, err
+ end
+
+ if not res.body or res.status ~= 200 then
+ return nil, 'status = ' .. res.status
+ end
+
+ local json_str = res.body
+ local data, err = core.json.decode(json_str)
+ if not data then
+ return nil, err
+ end
+ return data
+end
+
+
+local function get_url(request_uri, path)
+ return request(request_uri, path, nil, 'GET', nil)
+end
+
+
+local function post_url(request_uri, path, body)
+ return request(request_uri, path, body, 'POST', nil)
+end
+
+
+local function get_token_param(base_uri, username, password)
+ if not username or not password then
+ return ''
+ end
+
+ local args = { username = username, password = password}
+ local data, err = post_url(base_uri, auth_path .. '?' ..
ngx.encode_args(args), nil)
+ if err then
+ log.error('nacos login fail:', username, ' ', password, ' desc:', err)
+ return nil, err
+ end
+ return '&accessToken=' .. data.accessToken
+end
+
+
+local function get_base_uri()
+ local host = local_conf.discovery.nacos.host
+ -- TODO Add health check to get healthy nodes.
+ local url = host[math_random(#host)]
+ local auth_idx = str_find(url, '@')
+ local username, password
+ if auth_idx then
+ local protocol_idx = str_find(url, '://')
+ local protocol = string_sub(url, 1, protocol_idx + 2)
+ local user_and_password = string_sub(url, protocol_idx + 3, auth_idx -
1)
+ local arr = ngx_re.split(user_and_password, ':')
+ if #arr == 2 then
+ username = arr[1]
+ password = arr[2]
+ end
+ local other = string_sub(url, auth_idx + 1)
+ url = protocol .. other
+ end
+
+ if local_conf.discovery.nacos.prefix then
+ url = url .. local_conf.discovery.nacos.prefix
+ end
+
+ if str_byte(url, #url) ~= str_byte('/') then
+ url = url .. '/'
+ end
+
+ return url, username, password
+end
+
+
+local function iter_and_add_service(services, values)
+ if not values then
+ return
+ end
+
+ for _, value in core.config_util.iterate_values(values) do
+ local conf = value.value
+ if not conf then
+ goto CONTINUE
+ end
+
+ local up
+ if conf.upstream then
+ up = conf.upstream
+ else
+ up = conf
+ end
+
+ if up.discovery_type == 'nacos' then
+ core.table.insert(services, up.service_name)
+ end
+ ::CONTINUE::
+ end
+end
+
+
+local function get_nacos_services()
+ local services = {}
+
+ -- here we use lazy load to work around circle dependency
+ local get_upstreams = require('apisix.upstream').upstreams
+ local get_routes = require('apisix.router').http_routes
+ local get_services = require('apisix.http.service').services
+
+ local values = get_upstreams()
+ iter_and_add_service(services, values)
+ values = get_routes()
+ iter_and_add_service(services, values)
+ values = get_services()
+ iter_and_add_service(services, values)
+ return services
+end
+
+
+local function fetch_full_registry(premature)
+ if premature then
+ return
+ end
+
+ local up_apps = {}
+ local base_uri, username, password = get_base_uri()
+ local token_param, err = get_token_param(base_uri, username, password)
+ if err then
+ log.error('get_token_param error:', err)
+ if not applications then
+ applications = up_apps
+ end
+ return
+ end
+
+ local infos = get_nacos_services()
+ if #infos == 0 then
+ applications = up_apps
+ return
+ end
+
+ local data, err
+ for _, service_name in ipairs(infos) do
+ data, err = get_url(base_uri, instance_list_path .. service_name ..
token_param)
+ if err then
+ log.error('get_url:', instance_list_path, ' err:', err)
+ if not applications then
+ applications = up_apps
+ end
+ return
+ end
+
+ for _, host in ipairs(data.hosts) do
+ local nodes = up_apps[service_name]
+ if not nodes then
+ nodes = {}
+ up_apps[service_name] = nodes
+ end
+ core.table.insert(nodes, {
+ host = host.ip,
+ port = host.port,
+ weight = host.weight or default_weight,
+ })
+ end
+ end
+ applications = up_apps
+end
+
+
+function _M.nodes(service_name)
+ local logged = false
+ -- maximum waiting time: 5 seconds
+ local waiting_time = 5
+ local step = 0.1
+ while not applications and waiting_time > 0 do
+ if not logged then
+ log.warn('wait init')
+ logged = true
+ end
+ ngx.sleep(step)
+ waiting_time = waiting_time - step
+ end
+ return applications[service_name]
+end
+
+
+function _M.init_worker()
+ if not local_conf.discovery.nacos or
+ not local_conf.discovery.nacos.host or
#local_conf.discovery.nacos.host == 0 then
+ error('do not set nacos.host')
+ return
+ end
+
+ local ok, err = core.schema.check(schema, local_conf.discovery.nacos)
+ if not ok then
+ error('invalid nacos configuration: ' .. err)
+ return
+ end
+ default_weight = local_conf.discovery.nacos.weight
+ log.info('default_weight:', default_weight)
+ local fetch_interval = local_conf.discovery.nacos.fetch_interval
+ log.info('fetch_interval:', fetch_interval)
+ ngx_timer_at(0, fetch_full_registry)
+ ngx_timer_every(fetch_interval, fetch_full_registry)
+end
+
+
+function _M.dump_data()
+ return {config = local_conf.discovery.nacos, services = applications or {}}
+end
+
+
+return _M
diff --git a/ci/linux_openresty_common_runner.sh
b/ci/linux_openresty_common_runner.sh
index 34c3ecd..0716806 100755
--- a/ci/linux_openresty_common_runner.sh
+++ b/ci/linux_openresty_common_runner.sh
@@ -42,6 +42,37 @@ before_install() {
# start consul servers
docker run --rm --name consul_1 -d -p 8500:8500 consul:1.7 consul agent
-server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
docker run --rm --name consul_2 -d -p 8600:8500 consul:1.7 consul agent
-server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
+
+ # start nacos server
+ nohup docker network rm nacos_net > /dev/null 2>&1 &
+ nohup docker network create nacos_net > /dev/null 2>&1 &
+ # nacos no auth server - for test no auth
+ docker run --rm -d --name nacos_no_auth --network nacos_net --hostname
nacos2 --env NACOS_SERVERS="nacos1:8848 nacos2:8848" --env
PREFER_HOST_MODE=hostname --env MODE=cluster --env EMBEDDED_STORAGE=embedded
--env JVM_XMS=512m --env JVM_XMX=512m --env JVM_XMN=256m -p8858:8848
nacos/nacos-server:1.4.1
+ # nacos auth server - for test auth
+ docker run --rm -d --name nacos_auth --network nacos_net --hostname nacos1
--env NACOS_AUTH_ENABLE=true --env NACOS_SERVERS="nacos1:8848 nacos2:8848"
--env PREFER_HOST_MODE=hostname --env MODE=cluster --env
EMBEDDED_STORAGE=embedded --env JVM_XMS=512m --env JVM_XMX=512m --env
JVM_XMN=256m -p8848:8848 nacos/nacos-server:1.4.1
+ url="127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200" ]];
do
+ echo 'wait nacos server...'
+ sleep 1;
+ done
+ # register nacos service
+ rm -rf tmp
+ mkdir tmp
+ cd tmp
+ wget
https://raw.githubusercontent.com/api7/nacos-test-service/main/spring-nacos-1.0-SNAPSHOT.jar
+ curl
https://raw.githubusercontent.com/api7/nacos-test-service/main/Dockerfile |
docker build -t nacos-test-service:1.0-SNAPSHOT -f - .
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=1 -p 18001:18001 --name
nacos-service1 nacos-test-service:1.0-SNAPSHOT
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=2 -p 18002:18001 --name
nacos-service2 nacos-test-service:1.0-SNAPSHOT
+ url="127.0.0.1:18002/hello"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200" ]];
do
+ echo 'wait nacos service...'
+ sleep 1;
+ done
+ until [[ $(curl -s
"127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2" | grep
"APISIX-NACOS") ]]; do
+ echo 'wait nacos reg...'
+ sleep 1;
+ done
+ cd ..
}
do_install() {
diff --git a/ci/linux_tengine_runner.sh b/ci/linux_tengine_runner.sh
index b8f6a27..f48d90b 100755
--- a/ci/linux_tengine_runner.sh
+++ b/ci/linux_tengine_runner.sh
@@ -40,6 +40,37 @@ before_install() {
# start consul servers
docker run --rm --name consul_1 -d -p 8500:8500 consul:1.7 consul agent
-server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
docker run --rm --name consul_2 -d -p 8600:8500 consul:1.7 consul agent
-server -bootstrap-expect=1 -client 0.0.0.0 -log-level info
-data-dir=/consul/data
+
+ # start nacos server
+ nohup docker network rm nacos_net > /dev/null 2>&1 &
+ nohup docker network create nacos_net > /dev/null 2>&1 &
+ # nacos no auth server - for test no auth
+ docker run --rm -d --name nacos_no_auth --network nacos_net --hostname
nacos2 --env NACOS_SERVERS="nacos1:8848 nacos2:8848" --env
PREFER_HOST_MODE=hostname --env MODE=cluster --env EMBEDDED_STORAGE=embedded
--env JVM_XMS=512m --env JVM_XMX=512m --env JVM_XMN=256m -p8858:8848
nacos/nacos-server:1.4.1
+ # nacos auth server - for test auth
+ docker run --rm -d --name nacos_auth --network nacos_net --hostname nacos1
--env NACOS_AUTH_ENABLE=true --env NACOS_SERVERS="nacos1:8848 nacos2:8848"
--env PREFER_HOST_MODE=hostname --env MODE=cluster --env
EMBEDDED_STORAGE=embedded --env JVM_XMS=512m --env JVM_XMX=512m --env
JVM_XMN=256m -p8848:8848 nacos/nacos-server:1.4.1
+ url="127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200" ]];
do
+ echo 'wait nacos server...'
+ sleep 1;
+ done
+ # register nacos service
+ rm -rf tmp
+ mkdir tmp
+ cd tmp
+ wget
https://raw.githubusercontent.com/api7/nacos-test-service/main/spring-nacos-1.0-SNAPSHOT.jar
+ curl
https://raw.githubusercontent.com/api7/nacos-test-service/main/Dockerfile |
docker build -t nacos-test-service:1.0-SNAPSHOT -f - .
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=1 -p 18001:18001 --name
nacos-service1 nacos-test-service:1.0-SNAPSHOT
+ docker run -d --rm --network nacos_net --env SERVICE_NAME=APISIX-NACOS
--env NACOS_ADDR=nacos2:8848 --env SUFFIX_NUM=2 -p 18002:18001 --name
nacos-service2 nacos-test-service:1.0-SNAPSHOT
+ url="127.0.0.1:18002/hello"
+ until [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $url)" == "200" ]];
do
+ echo 'wait nacos service...'
+ sleep 1;
+ done
+ until [[ $(curl -s
"127.0.0.1:8858/nacos/v1/ns/service/list?pageNo=1&pageSize=2" | grep
"APISIX-NACOS") ]]; do
+ echo 'wait nacos reg...'
+ sleep 1;
+ done
+ cd ..
}
tengine_install() {
diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json
index 599a556..2721df5 100644
--- a/docs/en/latest/config.json
+++ b/docs/en/latest/config.json
@@ -158,6 +158,7 @@
"discovery",
"discovery/dns",
"discovery/consul_kv",
+ "discovery/nacos",
"discovery/eureka"
]
},
diff --git a/docs/en/latest/discovery.md b/docs/en/latest/discovery.md
index 97e511d..2abaf22 100644
--- a/docs/en/latest/discovery.md
+++ b/docs/en/latest/discovery.md
@@ -35,12 +35,14 @@ Common registries: Eureka, Etcd, Consul, Zookeeper, Nacos
etc.
## Supported discovery registries
-Currently we support Eureka/Consul and service discovery via DNS.
+Currently we support Eureka/Consul/Nacos and service discovery via DNS.
For service discovery via DNS, see [service discovery via
DNS](discovery/dns.md).
For Consul, see [service discovery via Consul](discovery/consul_kv.md)
+For Nacos, see [service discovery via Nacos](discovery/nacos.md)
+
For Eureka, see below.
## How to extend the discovery client?
diff --git a/docs/en/latest/discovery/nacos.md
b/docs/en/latest/discovery/nacos.md
new file mode 100644
index 0000000..5d5ea2b
--- /dev/null
+++ b/docs/en/latest/discovery/nacos.md
@@ -0,0 +1,101 @@
+---
+title: nacos
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Service discovery via Nacos
+
+This is experimental discovery module for Nacos.
+
+The performance of this module needs to be improved:
+
+1. avoid synchroning configuration in each workers. You can refer the
implementation in `consul_kv.lua`.
+2. send the request parallelly.
+
+### Configuration for Nacos
+
+Add following configuration in `conf/config.yaml` :
+
+```yaml
+discovery:
+ nacos:
+ host:
+ - "http://${username}:${password}@${host1}:${port1}"
+ prefix: "/nacos/v1/"
+ fetch_interval: 30 # default 30 sec
+ weight: 100 # default 100
+ timeout:
+ connect: 2000 # default 2000 ms
+ send: 2000 # default 2000 ms
+ read: 5000 # default 5000 ms
+```
+
+And you can config it in short by default value:
+
+```yaml
+discovery:
+ nacos:
+ host:
+ - "http://192.168.33.1:8848"
+```
+
+### Upstream setting
+
+Here is an example of routing a request with a URL of "/nacos/*" to a service
which named
"http://192.168.33.1:8848/nacos/v1/ns/instance/list?serviceName=APISIX-NACOS"
and use nacos discovery client in the registry :
+
+```shell
+$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY:
edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
+{
+ "uri": "/nacos/*",
+ "upstream": {
+ "service_name": "APISIX-NACOS",
+ "type": "roundrobin",
+ "discovery_type": "nacos"
+ }
+}'
+```
+
+The format response as below:
+
+```json
+{
+ "node": {
+ "key": "\/apisix\/routes\/1",
+ "value": {
+ "id": "1",
+ "create_time": 1615796097,
+ "status": 1,
+ "update_time": 1615799165,
+ "upstream": {
+ "hash_on": "vars",
+ "pass_host": "pass",
+ "scheme": "http",
+ "service_name": "APISIX-NACOS",
+ "type": "roundrobin",
+ "discovery_type": "nacos"
+ },
+ "priority": 0,
+ "uri": "\/nacos\/*"
+ }
+ },
+ "action": "set"
+}
+```
diff --git a/docs/zh/latest/README.md b/docs/zh/latest/README.md
index 40b93ef..641dcba 100644
--- a/docs/zh/latest/README.md
+++ b/docs/zh/latest/README.md
@@ -117,7 +117,7 @@ A/B 测试、金丝雀发布(灰度发布)、蓝绿部署、限流限速、抵
- **运维友好**
- OpenTracing 可观测性: 支持 [Apache Skywalking](plugins/skywalking.md) 和
[Zipkin](plugins/zipkin.md)。
- - 对接外部服务发现:除了内置的 etcd 外,还支持 `Consul` 和 `Nacos` 的 [DNS
发现模式](https://github.com/apache/apisix/issues/1731#issuecomment-646392129),以及
[Eureka](discovery.md)。
+ - 对接外部服务发现:除了内置的 etcd 外,还支持 [Consul](../../en/latest/discovery/consul_kv.md)
和 [Nacos](../../en/latest/discovery/nacos.md),以及 [Eureka](discovery.md)。
- 监控和指标: [Prometheus](plugins/prometheus.md)
- 集群:APISIX 节点是无状态的,创建配置中心集群请参考 [etcd Clustering
Guide](https://etcd.io/docs/v3.4.0/op-guide/clustering/)。
- 高可用:支持配置同一个集群内的多个 etcd 地址。
diff --git a/docs/zh/latest/discovery.md b/docs/zh/latest/discovery.md
index 261f242..20105e1 100644
--- a/docs/zh/latest/discovery.md
+++ b/docs/zh/latest/discovery.md
@@ -53,6 +53,8 @@ title: 集成服务发现注册中心
Consul 的支持见 [基于 Consul 的服务支持发现](../../en/latest/discovery/consul_kv.md)
+Nacos 的支持见 [基于 Nacos 的服务支持发现](../../en/latest/discovery/nacos.md)
+
Eureka 的支持方式见下文。
## 如何扩展注册中心?
diff --git a/t/discovery/nacos.t b/t/discovery/nacos.t
new file mode 100644
index 0000000..60f60cb
--- /dev/null
+++ b/t/discovery/nacos.t
@@ -0,0 +1,253 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+log_level('info');
+no_root_location();
+no_shuffle();
+
+our $yaml_config = <<_EOC_;
+apisix:
+ node_listen: 1984
+ config_center: yaml
+ enable_admin: false
+discovery:
+ nacos:
+ host:
+ - "http://127.0.0.1:8858"
+ prefix: "/nacos/v1/"
+ fetch_interval: 1
+ weight: 1
+ timeout:
+ connect: 2000
+ send: 2000
+ read: 5000
+
+_EOC_
+
+our $yaml_auth_config = <<_EOC_;
+apisix:
+ node_listen: 1984
+ config_center: yaml
+ enable_admin: false
+discovery:
+ nacos:
+ host:
+ - "http://nacos:nacos\@127.0.0.1:8848"
+ prefix: "/nacos/v1/"
+ fetch_interval: 1
+ weight: 1
+ timeout:
+ connect: 2000
+ send: 2000
+ read: 5000
+
+_EOC_
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: get APISIX-NACOS info from NACOS - no auth
+--- yaml_config eval: $::yaml_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ upstream:
+ service_name: APISIX-NACOS
+ discovery_type: nacos
+ type: roundrobin
+#END
+--- pipelined_requests eval
+[
+ "GET /hello",
+ "GET /hello",
+]
+--- response_body_like eval
+[
+ qr/server [1-2]/,
+ qr/server [1-2]/,
+]
+--- no_error_log
+[error, error]
+
+
+
+=== TEST 2: error service_name name - no auth
+--- yaml_config eval: $::yaml_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ upstream:
+ service_name: APISIX-NACOS-DEMO
+ discovery_type: nacos
+ type: roundrobin
+
+#END
+--- request
+GET /hello
+--- error_code: 503
+
+
+
+=== TEST 3: get APISIX-NACOS info from NACOS - auth
+--- yaml_config eval: $::yaml_auth_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ upstream:
+ service_name: APISIX-NACOS
+ discovery_type: nacos
+ type: roundrobin
+
+#END
+--- pipelined_requests eval
+[
+ "GET /hello",
+ "GET /hello",
+]
+--- response_body_like eval
+[
+ qr/server [1-2]/,
+ qr/server [1-2]/,
+]
+--- no_error_log
+[error, error]
+
+
+
+=== TEST 4: error service_name name - auth
+--- yaml_config eval: $::yaml_auth_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ upstream:
+ service_name: APISIX-NACOS-DEMO
+ discovery_type: nacos
+ type: roundrobin
+
+#END
+--- request
+GET /hello
+--- error_code: 503
+
+
+
+=== TEST 5: get APISIX-NACOS info from NACOS - configured in services
+--- yaml_config eval: $::yaml_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ service_id: 1
+services:
+ -
+ id: 1
+ upstream:
+ service_name: APISIX-NACOS
+ discovery_type: nacos
+ type: roundrobin
+#END
+--- pipelined_requests eval
+[
+ "GET /hello",
+ "GET /hello",
+]
+--- response_body_like eval
+[
+ qr/server [1-2]/,
+ qr/server [1-2]/,
+]
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: get APISIX-NACOS info from NACOS - configured in upstreams + etcd
+--- extra_yaml_config
+discovery:
+ nacos:
+ host:
+ - "http://127.0.0.1:8858"
+ fetch_interval: 1
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/upstreams/1',
+ ngx.HTTP_PUT,
+ [[{
+ "service_name": "APISIX-NACOS",
+ "discovery_type": "nacos",
+ "type": "roundrobin"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "upstream_id": 1
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 7: hit
+--- extra_yaml_config
+discovery:
+ nacos:
+ host:
+ - "http://127.0.0.1:8858"
+ fetch_interval: 1
+--- pipelined_requests eval
+[
+ "GET /hello",
+ "GET /hello",
+]
+--- response_body_like eval
+[
+ qr/server [1-2]/,
+ qr/server [1-2]/,
+]
+--- no_error_log
+[error]