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

feat: add nacos support #3820

merged 66 commits into from
Apr 21, 2021

Conversation

benx203
Copy link
Contributor

@benx203 benx203 commented Mar 13, 2021

feat:add nacos support

add nacos support
Copy link
Member

@spacewander spacewander left a comment

Choose a reason for hiding this comment

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

Please add test under t/discovery.

@spacewander
Copy link
Member

@benx203 benx203 changed the title add nacos support feat:add nacos support Mar 15, 2021
@spacewander spacewander changed the title feat:add nacos support feat: add nacos support Mar 15, 2021
@spacewander
Copy link
Member

Please make sure it can pass the lint.

discovery:
nacos:
host:
- "http://192.168.33.1:8848"
Copy link
Member

Choose a reason for hiding this comment

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

Why don't mention the user / password feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because Nacos not need login yet.

},
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.


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.

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
Copy link
Member

Choose a reason for hiding this comment

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

Where should I configure service_list_path? There is neither schema nor doc for 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.

I will fix it.

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
Copy link
Member

Choose a reason for hiding this comment

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

ditto

end

local up_apps = core.table.new(0, 0)
local data = get_url(base_uri,basic_auth,service_list_path)
Copy link
Member

Choose a reason for hiding this comment

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

Need space after the comma.
And please check the returned value of get_url.

log.info("nacos uri:", request_uri, ".")
local url = request_uri .. path
local headers = core.table.new(0, 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

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?

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("/")

apisix/discovery/nacos.lua Outdated Show resolved Hide resolved
fetch_interval: 30 # default 30 sec
weight: 100 # default 100
timeout:
connect: 2000 # default 2000
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 mention the unit is ms.

--- pipelined_requests eval
[
"POST /nacos/v1/ns/instance?port=18001&healthy=true&ip=127.0.0.1&weight=1.0&serviceName=APISIX-NACOS&encoding=GBK&enabled=true",
"DELETE /nacos/v1/ns/service?serviceName=APISIX-NACOS",
Copy link
Member

Choose a reason for hiding this comment

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

Why POST and then DELETE?

Copy link
Contributor Author

@benx203 benx203 Mar 23, 2021

Choose a reason for hiding this comment

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

If has no data,delete return code is 400,if has data,delete return code is 200.

Copy link
Member

Choose a reason for hiding this comment

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

But it is not the feature of APISIX. We don't need to test nacos itself.

#END
--- request
GET /hello
--- error_code: 200
Copy link
Member

Choose a reason for hiding this comment

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

We need to show if server 1 or server 2 is hit.
And we need a test to show the default weight

```shell
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d '
{
"uri": "/nacos/**",
Copy link
Member

Choose a reason for hiding this comment

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

Use /nacos/* is enough?

@@ -40,6 +40,9 @@ 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
docker run --rm --name nacos_1 -d --env PREFER_HOST_MODE=hostname --env MODE=standalone --env JVM_XMS=512m --env JVM_XMX=512m --env JVM_XMN=256m -p8848:8848 nacos/nacos-server:latest
Copy link
Member

Choose a reason for hiding this comment

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

We need test the user/password feature.

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 add NACOS_AUTH_ENABLE=true, file nacos.t need login too.
Could you give me a t file login demo?

Copy link
Member

Choose a reason for hiding this comment

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

You can use different conf in different tests, one for auth success and another for auth fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How to check nacos in discovery plugin whether authed?

Copy link
Member

Choose a reason for hiding this comment

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

If the auth enable nacos can be used, the auth is successful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But if nacos start with auth mode,in nacos.t add services to nacos need send auth token.
How can i save auth token in nacos.t?

Copy link
Member

Choose a reason for hiding this comment

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

}


local function str_split (inputstr, sep)
Copy link
Member

Choose a reason for hiding this comment

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

Should use existent API:

local levels = ngx_re.split(cache_levels, ":")

prefix = {type = "string", default = "/nacos/v1/"},
auth_path = {type = "string", default = "auth/login"},
service_list_path = {type = "string", default = "ns/service/list?pageNo=1&pageSize=20"},
instance_list_path = {type = "string", default = "ns/instance/list?serviceName="},
Copy link
Member

Choose a reason for hiding this comment

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

Need a test to check the user-defined paths.
BTW, in what situation people need to redefine the paths?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When Nacos api change.

Copy link
Member

Choose a reason for hiding this comment

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

@benx203
I think we can remove it now and add it if needed.
We need to add a doc and test for this feature if you insist to keep it.
(I don't recommend to do that as this PR is big enough)

apisix/discovery/nacos.lua Show resolved Hide resolved
fetch_interval = {type = "integer", minimum = 1, default = 30},
prefix = {type = "string", default = "/nacos/v1/"},
auth_path = {type = "string", default = "auth/login"},
service_list_path = {type = "string", default = "ns/service/list?pageNo=1&pageSize=20"},
Copy link
Member

Choose a reason for hiding this comment

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

Only return the first 20 services is not a good idea.
We need to iterate the upstreams and build a service list from those that have nacos discovery_type.
Like this:

values = get_upstreams()
else
return 400, {error_msg = str_format("invalid src type %s", src_type)}
end
local info, err = iter_and_find_healthcheck_info(values, src_type, src_id)

You might need some lazy load trick to break the dependent cycle.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

end

for _, host in ipairs(data.hosts) do
if tostring(host.valid) == 'true' and
Copy link
Member

Choose a reason for hiding this comment

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

I set up the environment and debug it myself.
Here is the host: {"enabled":true,"instanceHeartBeatTimeOut":15000,"ipDeleteTimeout":30000,"instanceHeartBeatInterval":5000,"metadata":{"preserved.register.source":"SPRING_CLOUD"},"port":18001,"serviceName":"DEFAULT_GROUP@@APISIX-NACOS","instanceId":"192.168.65.3#18001#DEFAULT#DEFAULT_GROUP@@APISIX-NACOS","healthy":true,"ip":"192.168.65.3","ephemeral":true,"clusterName":"DEFAULT","weight":1}

There is not valid field. The response doesn't match their API doc: https://nacos.io/en-us/docs/open-api.html

I am not sure if it is because the latest Nacos has changed the API. Maybe we need to use the tagged nacos image 2.0.0 instead of the latest one?

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:latest
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better to use nacos/nacos-server:2.0.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The nacos version should be 1.4.1,2.0.0 just released few days ago.

Copy link
Member

@spacewander spacewander Apr 8, 2021

Choose a reason for hiding this comment

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

@benx203
Why can't we support 2.0.0 directly? Is 2.0.0 incompatible with 1.4.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.

I think better way is support both 2.0.0 and 1.4.1.
Maybe support 2.0.0 later?

rm -rf tmp
mkdir tmp
cd tmp
wget https://raw.githubusercontent.com/benx203/nacos-test-service/main/spring-nacos-1.0-SNAPSHOT.jar
Copy link
Member

Choose a reason for hiding this comment

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

@benx203
Would you also submit the Java code to https://github.com/benx203/nacos-test-service? Thanks!

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.

for _, host in ipairs(data.hosts) do
if tostring(host.valid) == 'true' and
tostring(host.healthy) == 'true' and
tostring(host.enabled) == 'true' then
Copy link
Member

Choose a reason for hiding this comment

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

One more question: why we need to filter the result according to those fields?

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 query all records,need to filter.
But now should not need any more.

Signed-off-by: spacewander <spacewanderlzx@gmail.com>
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
@spacewander
Copy link
Member

@benx203
I will work on this PR and fulfill my promise in a few days. Please be patient.

@benx203
Copy link
Contributor Author

benx203 commented Apr 9, 2021

@spacewander
OK.Take your time.

Signed-off-by: spacewander <spacewanderlzx@gmail.com>
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
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", default = "/nacos/v1/"},
Copy link
Member

Choose a reason for hiding this comment

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

ditto



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 url = request_uri .. path
log.info("request url:", url)
local headers = core.table.new(0, 0)
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



local function get_token_param(base_uri, username, password)
if username and password 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 style:

if not username or not password then
    return ""
end

...


local function get_token_param(base_uri, username, password)
if username and password then
local data, err = post_url(base_uri, auth_path .. "?username=" .. username
Copy link
Member

Choose a reason for hiding this comment

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

I think we should call ngx.encode_args for username and password



local function iter_and_add_service(services, values)
if not values then
Copy link
Member

Choose a reason for hiding this comment

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

bad indentation

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, "@")
Copy link
Member

Choose a reason for hiding this comment

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

we'd better use the plain text model, better performance.

https://stackoverflow.com/questions/18973019/is-there-a-lua-string-find-without-pattern


function _M.nodes(service_name)
local logged = false
while not applications do
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to set a maximum waiting time , eg: 5 seconds

@moonming
Copy link
Member

@benx203 thanks for your contribution.
You can create a new PR to fix the code style issue.
And it will be better if you can add support Nacos in the README.md to let more people know about this feature.

@benx203
Copy link
Contributor Author

benx203 commented Apr 15, 2021

@benx203 thanks for your contribution.
You can create a new PR to fix the code style issue.
And it will be better if you can add support Nacos in the README.md to let more people know about this feature.

OK,i will try,thank you all.

@@ -275,41 +273,33 @@ end


function _M.nodes(service_name)
local logged = false
Copy link
Member

Choose a reason for hiding this comment

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

The original code is good. We just need to set a 5s limitation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just keep simple.

Copy link
Member

Choose a reason for hiding this comment

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

The lazy init is important as the sync is slow.

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 rollback the code.

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, '@', 1, true)
Copy link
Member

Choose a reason for hiding this comment

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

The core.string.find is already the plain string find... 😅
Not all the suggestion from the reviewer is correct.

local values = get_upstreams()
iter_and_add_service(services, values)
values = get_routes()
iter_and_add_service(services, values)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

By the way,Are there 'discovery_type' config in service and route?
@spacewander

Copy link
Member

Choose a reason for hiding this comment

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

Those can have embedded upstream field.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see,thanks.

benx203 and others added 5 commits April 19, 2021 16:48
Signed-off-by: spacewander <spacewanderlzx@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants