需求:
项目中用到了apisix做网关,apisix 支持配置注册中心,主动发现注册中心的服务。
于是我想到了直接通过 apisix 获取注册的服务列表,就不用自己再写一套这个拉取服务的逻辑了。
查看了apisix 的源码,发现数据并没有存放在 etcd 数据库,而是在内存中。
这里我主要讲一下,如何通过 apisix 开放一个接口,供外部访问内部数据。
前置知识
1.1注册中心配置
apixi 可以在 conf/config.yml 配置注册中心地址,以 nacos 为例:
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
其它注册中心配置,请参照:官网
1.2 自定义插件
public api 需要和插件在一起才能使用, 如果需要自定义插件,可以参考 apisix 自定义认证插件.
public api
概述
public-api 插件可用于通过创建路由的方式暴露用户自定义的 API,默认情况下,在自定义插件中添加的公共 API 不对外暴露的,你需要手动配置一个路由并启用 public-api 插件。
以 jwt-auth 插件为例,你可以使用 jwt-auth 插件创建一个公共 API 端点 /apisix/plugin/jwt/sign 用于 JWT 认证:
基本步骤:
- 创建一个路由,并启用 jwt-auth 插件:
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/r1' \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
-H 'Content-Type: application/json' \
-d '{
"uri": "/apisix/plugin/jwt/sign",
"plugins": {
"public-api": {}
}
}'
- 之后就可以通过调用它来获取 token 了
curl 'http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key'
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NTk0Mjg1MzIsImtleSI6InVzZXIta2V5In0.NhrWrO-da4kXezxTLdgFBX2rJA2dF1qESs8IgmwhNd0
- 对应的源码
服务发现 public api
自定义插件
如果 public api 有自定义插件的需求, 可以参考 apisix 自定义认证插件, 也可以参考我另一个文章,apsix 自定义插件
这里依旧以 jwt-auth 为例.
插件测试接口
先简单写个输出:
local function hello()
local args = ngx.req.get_uri_args()
if args["json"] then
return 200, {msg = "world"}
else
return 200, "world\n"
end
end
function _M.api()
return {
{
methods = {"GET"},
uri = "/apisix/plugin/jwt/sign",
handler = hello,
}
}
end
插件热加载:
使用此命令无需重启 apisix
curl http://127.0.0.1:9180/apisix/admin/plugins/reload -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT
输出:
world
服务发现api实现
-- 列出配置的注册中心的所有数据
local function get_discovery_services()
local response = {}
local data
local discovery = require("apisix.discovery.init").discovery
local discovery_type = require("apisix.core.config_local").local_conf().discovery
core.log.error("[discovery_type] is ", core.json.encode(discovery_type))
for key, _ in pairs(discovery_type) do
local dis_mod = discovery[key]
local dump_data = dis_mod.dump_data
if dump_data then
data = dump_data();
core.log.error("get_table_items: [key] is ", core.json.encode(key))
core.log.error("get_table_items: [dump_data] is ", core.json.encode(data, true))
end
-- 将数据保存到t中
response[key]=data
data=nil
end
core.log.error("get_table_items: [dump_data] is ", core.json.encode(response, true))
return 200, core.json.encode(response)
end
--对外暴露的api
function _M.api()
return {
{
methods = {"GET"},
uri = "/apisix/plugin/jwt/sign",
handler = get_discovery_services,
}
}
end
测试
接口测试:
curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i
结果:
[root@HOST example]# curl http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i
HTTP/1.1 200 OK
Date: Thu, 16 May 2024 03:21:21 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.7.0
{"nacos":{"services":{"xx":{"xx":{"xx-gateway":[{"host":"10.244.xx.xxx","weight":1,"port":8080}]}}},"config":{"host":["http://nacos:nacos@10.198.xx.xx:8848"],"prefix":"/nacos/v1/","weight":100,"secret_key":"","fetch_interval":30,"access_key":"","timeout":{"connect":2000,"send":2000,"read":5000}}}}
返参如下:
对于多注册中心的配置,这里也是适用的。
参考:https://apisix.apache.org/zh/docs/apisix/plugins/public-api/