Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add nacos support #3820

Merged
merged 66 commits into from
Apr 21, 2021
Merged
Changes from 1 commit
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
baab627
add nacos support
benx203 Mar 13, 2021
e053355
add docs
benx203 Mar 15, 2021
5b14149
fix lint warnings
benx203 Mar 18, 2021
613c221
fix test
benx203 Mar 18, 2021
f0b0942
fix nacos
benx203 Mar 18, 2021
221ee3a
fix nacos test script
benx203 Mar 19, 2021
8e3e6a5
fix
benx203 Mar 19, 2021
69055a0
fix nacos - support auth
benx203 Mar 19, 2021
e412a70
fix
benx203 Mar 19, 2021
9b48317
fix
benx203 Mar 19, 2021
d8f819b
fix
benx203 Mar 19, 2021
46fe9fd
fix
benx203 Mar 20, 2021
9e51570
reindex
benx203 Mar 20, 2021
4a0dac6
fix env script
benx203 Mar 21, 2021
bbc3f1f
fix env script
benx203 Mar 21, 2021
b153240
fix env script
benx203 Mar 21, 2021
962be01
fix test script
benx203 Mar 22, 2021
26de944
fix test script
benx203 Mar 22, 2021
c1e6f00
fix
benx203 Mar 22, 2021
95ab302
fix
benx203 Mar 22, 2021
63eba52
fix
benx203 Mar 23, 2021
df1d54f
fix
benx203 Mar 23, 2021
3ea6a66
fix
benx203 Mar 29, 2021
d1f3bef
fix
benx203 Mar 29, 2021
ec0c16d
Merge branch 'master' of https://github.com/apache/apisix
benx203 Apr 1, 2021
397a197
fix
benx203 Apr 1, 2021
dcb76c2
fix
benx203 Apr 1, 2021
14de476
fix
benx203 Apr 1, 2021
b53f4e9
fix
benx203 Apr 2, 2021
dccd644
fix
benx203 Apr 2, 2021
33e7c2e
fix
benx203 Apr 2, 2021
ddff0f5
fix
benx203 Apr 2, 2021
3ebf626
fix
benx203 Apr 2, 2021
147c088
modify test
benx203 Apr 2, 2021
d55effc
fix
benx203 Apr 2, 2021
ea93f5e
fix
benx203 Apr 6, 2021
922ac50
trigger run test script
benx203 Apr 6, 2021
5a91e81
fix test
benx203 Apr 7, 2021
0d1ef67
fix
benx203 Apr 7, 2021
7398fbb
fix
benx203 Apr 7, 2021
74f5512
fix
benx203 Apr 7, 2021
b7d356c
fix
benx203 Apr 7, 2021
4fa00d7
rerun test
benx203 Apr 7, 2021
638cdc9
fix
benx203 Apr 7, 2021
e24d641
fix
benx203 Apr 7, 2021
dce8163
fix test
benx203 Apr 8, 2021
04a42a2
fix
benx203 Apr 8, 2021
846fc86
fix test
benx203 Apr 8, 2021
952aa04
fix test
benx203 Apr 8, 2021
5ea5414
fix
benx203 Apr 8, 2021
e1a9f13
Merge remote-tracking branch 'upstream/master'
benx203 Apr 8, 2021
6bbcaf1
debug test
benx203 Apr 8, 2021
f150a72
debug test
benx203 Apr 8, 2021
9ac15df
debug test
benx203 Apr 8, 2021
c97c0a2
fork the repo to prevent the original repo deleted by accident
spacewander Apr 9, 2021
4936b19
tweak
spacewander Apr 9, 2021
fb69097
WIP
spacewander Apr 9, 2021
6171c2e
Merge branch 'master' into benx203_master
spacewander Apr 14, 2021
83d7500
fix lint
spacewander Apr 14, 2021
5af1d9e
fix
benx203 Apr 19, 2021
69408be
fix
benx203 Apr 19, 2021
dfd6921
fix
benx203 Apr 19, 2021
8b196f3
fix
benx203 Apr 19, 2021
09767bd
fix
benx203 Apr 19, 2021
660d2b6
trigger test
benx203 Apr 20, 2021
407fb42
style
spacewander Apr 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions apisix/discovery/nacos.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
--
-- 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 local_conf = require("apisix.core.config_local").local_conf()
local http = require("resty.http")
local core = require("apisix.core")
local ipmatcher = require("resty.ipmatcher")
local ipairs = ipairs
local tostring = tostring
local type = type
local math_random = math.random
local error = error
local ngx = ngx
local ngx_timer_at = ngx.timer.at
local ngx_timer_every = ngx.timer.every
local string_sub = string.sub
local str_find = core.string.find
local log = core.log

local default_weight
local applications
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can initialize applications = {} so that we can save many codes like:

        if not applications then
            applications = up_apps
        end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If use applications = {},run test could fail cause maybe call _M.nodes method before finish init.

local service_list_path
local instance_list_path

local schema = {
type = "object",
properties = {
host = {
type = "array",
minItems = 1,
items = {
type = "string",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a pattern to limit the string to the host format

},
},
fetch_interval = {type = "integer", minimum = 1, default = 30},
prefix = {type = "string"},
weight = {type = "integer", minimum = 0},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing default for weight?
And the minimum weight should be 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK,I copy from eureka.

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},
}
},
},
required = {"host"}
}


local _M = {
version = 0.1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this version is useless, we can drop it

}


local function service_info()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of the function is not good.
We can split it into two functions, get_base_uri and refresh_token_param.
And there is no need to use the global variable base_uri, we pass it as an argument in many functions.

local host = local_conf.discovery and
local_conf.discovery.nacos and local_conf.discovery.nacos.host
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this style is better:

Suggested change
local host = local_conf.discovery and
local_conf.discovery.nacos and local_conf.discovery.nacos.host
local host = local_conf.discovery and local_conf.discovery.nacos
and local_conf.discovery.nacos.host

if not host then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to check the host repeatedly. We can check it in the schema.

log.error("do not set nacos.host")
return
end

local basic_auth
-- TODO Add health check to get healthy nodes.
local url = host[math_random(#host)]
local auth_idx = str_find(url, "@")
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 other = string_sub(url, auth_idx + 1)
url = protocol .. other
basic_auth = "Basic " .. ngx.encode_base64(user_and_password)
end
if local_conf.discovery.nacos.prefix then
url = url .. local_conf.discovery.nacos.prefix
end
if string_sub(url, #url) ~= "/" then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use str_byte(uri, #uri) == str_byte("/")

url = url .. "/"
end

return url, basic_auth
end


local function request(request_uri, basic_auth, method, path, query, body)
log.info("nacos uri:", request_uri, ".")
local url = request_uri .. path
local headers = core.table.new(0, 5)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why choose 5?

headers['Connection'] = 'Keep-Alive'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need it for HTTP 1.1

headers['Accept'] = 'application/json'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the string object, I think we use ", it is easier for reading.

headers["Accept"] = "application/json"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and please fix the similar points


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
-- log.warn(method, url, body)
headers['Content-Type'] = 'application/json'
end

local httpc = http.new()
local timeout = local_conf.discovery.nacos.timeout
local connect_timeout = timeout and timeout.connect or 2000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value will be injected via schema check. We don't need to assign it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

local send_timeout = timeout and timeout.send or 2000
local read_timeout = timeout and timeout.read or 5000
log.info("connect_timeout:", connect_timeout, ", send_timeout:", send_timeout,
", read_timeout:", read_timeout, ".")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad style, this would be better:

Suggested change
log.info("connect_timeout:", connect_timeout, ", send_timeout:", send_timeout,
", read_timeout:", read_timeout, ".")
log.info("connect_timeout:", connect_timeout, ", send_timeout:", send_timeout,
", read_timeout:", read_timeout, ".")

httpc:set_timeouts(connect_timeout, send_timeout, read_timeout)
return httpc:request_uri(url, {
version = 1.1,
method = method,
headers = headers,
query = query,
body = body,
ssl_verify = false,
})
end


local function get_url(request_url,basic_auth,path)
local res, err = request(request_url, basic_auth, "GET", path)
if not res then
log.error("failed to fetch registry", err)
return
spacewander marked this conversation as resolved.
Show resolved Hide resolved
end

if not res.body or res.status ~= 200 then
log.error("failed to fetch registry, status = ", res.status)
return
end

local json_str = res.body
local data, err = core.json.decode(json_str)
if not data then
log.error("invalid response body: ", json_str, " err: ", err)
return
end
return data
end


local function fetch_full_registry(premature)
if premature then
return
end

local base_uri, basic_auth = service_info()
if not base_uri then
return
end

local up_apps = core.table.new(0, 0)
local data = get_url(base_uri,nil,service_list_path)
if tostring(data.count) == "0" then
applications = up_apps
return
end

for _, service_name in ipairs(data.doms) do
data = get_url(base_uri,nil,instance_list_path .. service_name)
for _, host in ipairs(data.hosts) do
if tostring(host.valid) == 'true' and tostring(host.healthy) == 'true' and tostring(host.enabled) == 'true' then
local nodes = up_apps[service_name]
if not nodes then
nodes = core.table.new(0, 0)
up_apps[service_name] = nodes
end
core.table.insert(nodes, {
host = host.ip,
port = host.port,
weight = host.weight or default_weight,
})
end
end
end
applications = up_apps
end


function _M.nodes(service_name)
if not applications then
log.error("failed to fetch nodes for : ", service_name)
return
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 or 100
log.info("default_weight:", default_weight, ".")
local fetch_interval = local_conf.discovery.nacos.fetch_interval or 30
log.info("fetch_interval:", fetch_interval, ".")
service_list_path = local_conf.discovery.nacos.service_list_path or 'ns/service/list?pageNo=1&pageSize=20'
instance_list_path = local_conf.discovery.nacos.instance_list_path or 'ns/instance/list?serviceName='
ngx_timer_at(0, fetch_full_registry)
ngx_timer_every(fetch_interval, fetch_full_registry)
end


return _M