悟 Kong(那美克星篇)
May 2, 2020 22:45 · 1872 words · 4 minute read
Kong 是什么?
Kong 是可伸缩的 API 网关,是一个在 Ningx 中运行的 Lua 应用程序,是对数据库抽象的实现,是在 OpenResty 之上封装的一层 API,是框架也是开发工具集,是 CLI 也是服务。
我们即可以按照官方教程 https://docs.konghq.com/2.0.x/getting-started/quickstart/ 来自行手动部署,也可以使用以下脚本快速拉起一个以 PostgreSQL 作为数据库的 Kong 环境。
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/crazytaxii/king-kong/master/tools/deploy.sh)"
Hello Kong
Kong 源码 repo: https://github.com/kong/kong
我们从 Kong 项目的源码结构开始:
$ ll kong
total 1512
-rw-r--r-- 1 crazytaxii staff 228K Apr 13 19:30 CHANGELOG.md
-rw-r--r-- 1 crazytaxii staff 3.2K Apr 13 19:30 CODE_OF_CONDUCT.md
-rw-r--r-- 1 crazytaxii staff 24K Apr 13 19:30 CONTRIBUTING.md
-rw-r--r-- 1 crazytaxii staff 229K Apr 13 19:30 COPYRIGHT
-rw-r--r-- 1 crazytaxii staff 6.2K Apr 13 19:30 DEVELOPER.md
-rw-r--r-- 1 crazytaxii staff 24K Apr 13 19:30 Jenkinsfile
-rw-r--r-- 1 crazytaxii staff 11K Apr 13 19:30 LICENSE
-rw-r--r-- 1 crazytaxii staff 3.7K Apr 13 19:30 Makefile
-rw-r--r-- 1 crazytaxii staff 11K Apr 13 19:30 README.md
-rw-r--r-- 1 crazytaxii staff 117K Apr 13 19:30 UPGRADE.md
drwxr-xr-x 4 crazytaxii staff 128B Apr 13 19:30 autodoc
drwxr-xr-x 4 crazytaxii staff 128B Apr 13 19:30 bin
drwxr-xr-x 28 crazytaxii staff 896B Apr 13 19:30 kong
-rw-r--r-- 1 crazytaxii staff 19K Apr 13 19:30 kong-2.0.3-0.rockspec
-rw-r--r-- 1 crazytaxii staff 62K Apr 13 19:30 kong.conf.default
drwxr-xr-x 6 crazytaxii staff 192B Apr 13 19:30 scripts
drwxr-xr-x 11 crazytaxii staff 352B Apr 13 19:30 spec
drwxr-xr-x 7 crazytaxii staff 224B Apr 13 19:30 t
- bin 路径下存放了两个拥有执行权限的文件,分别是 Kong CLI 与 Lua 单元测试工具 busted。
- kong 同名路径下是所有相关源码
- api - Kong API 层实现
- db - Kong 的 DAO 实现
- pdk - 全名为 Plugin Development Kit 也就是 Kong 插件开发套件
- plugin - Kong 原生插件源码,这些代码对自行实现 Kong 插件很有帮助
- cmd - Kong 的 CLI 实现
- t - OpenResty 的测试案例,测试框架为 Perl 开发的 test-nginx
- spec 路径下主要是单元/集成测试还有 Kong 插件测试的源码,可以用 bin 路径下的 busted 直接来运行
Kong CLI
我们可以通过 Kong CLI 的 start
和 stop
命令来启动或者停止 Kong 网关服务,还可以通过 help 命令来查看 Kong CLI 更多的使用方法。
Kong CLI 本体:https://github.com/kong/kong/blob/master/bin/kong
#!/usr/bin/env resty
setmetatable(_G, nil)
pcall(require, "luarocks.loader")
package.path = (os.getenv("KONG_LUA_PATH_OVERRIDE") or "") .. "./?.lua;./?/init.lua;" .. package.path
require("kong.cmd.init")(arg)
是一个 resty 脚本。
pcall
(protected call) 能够捕获被包装代码执行时的任何错误,即使这一步出问题程序程序也可以继续执行下去package.path
这个全局变量指定了代码搜索路径,Lua 启动后首先会以LUA_PATH
环境变量的值来对其初始化。- 我们带上的 kong 命令,会以参数的方式传递到 cmd 模块
Kong 实现 CLI 命令参数解析并调用相应模块的方式颇为巧妙:
$ kong help
No such command: help
Usage: kong COMMAND [OPTIONS]
The available commands are:
check
config
health
hybrid
migrations
prepare
quit
reload
restart
roar
start
stop
version
$ ll kong/cmd
total 128
-rw-r--r-- 1 crazytaxii staff 659B Apr 13 19:30 check.lua
-rw-r--r-- 1 crazytaxii staff 4.7K Apr 13 19:30 config.lua
-rw-r--r-- 1 crazytaxii staff 1.5K Apr 13 19:30 health.lua
-rw-r--r-- 1 crazytaxii staff 2.7K Apr 13 19:30 hybrid.lua
-rw-r--r-- 1 crazytaxii staff 2.2K Apr 13 19:30 init.lua
-rw-r--r-- 1 crazytaxii staff 5.1K Apr 13 19:30 migrations.lua
-rw-r--r-- 1 crazytaxii staff 997B Apr 13 19:30 prepare.lua
-rw-r--r-- 1 crazytaxii staff 2.2K Apr 13 19:30 quit.lua
-rw-r--r-- 1 crazytaxii staff 1.4K Apr 13 19:30 reload.lua
-rw-r--r-- 1 crazytaxii staff 1.4K Apr 13 19:30 restart.lua
-rw-r--r-- 1 crazytaxii staff 386B Apr 13 19:30 roar.lua
-rw-r--r-- 1 crazytaxii staff 3.1K Apr 13 19:30 start.lua
-rw-r--r-- 1 crazytaxii staff 1.1K Apr 13 19:30 stop.lua
drwxr-xr-x 9 crazytaxii staff 288B Apr 13 19:30 utils
-rw-r--r-- 1 crazytaxii staff 597B Apr 13 19:30 version.lua
cmd 路径下的 lua 源码文件名与 Kong CLI 命令一一对应。
https://github.com/kong/kong/blob/master/kong/cmd/init.lua
return function(args)
-- ...
local cmd = require("kong.cmd." .. cmd_name)
local cmd_lapp = cmd.lapp
local cmd_exec = cmd.execute
xpcall(function() cmd_exec(args) end, function(err)
-- ...
pl_app.quit(nil, true)
end)
end
而每个命令模块都实现了 lapp
字符串与 execute
方法,我们启动 Kong 时输入 kong start -c /path/to/kong.conf
就会执行 kong/cmd/start.lua 文件中已经定义好的 execute
函数。xpcall
方法要带上一个错误处理函数,用于处理第一个方法参数执行时抛出的错误。
通过 https://github.com/kong/kong/blob/master/kong/cmd/start.lua 源码我们可以清楚地知道 Kong 在启动时所有的步骤:
- 加载并解析配置文件
- 断言是否有 Nginx(OpenResty) 进程在运行
- 连接 PostgreSQL 或者 Cassandra 数据库
- 通过源码中内置的模板渲染出一份 nginx.conf 配置文件
- 检查数据表是否最新
- 启动 Nginx(OpenResty) 进程
ps aux | grep nginx
crazytaxii 48173 0.1 0.1 5545908 35064 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48175 0.1 0.1 5291956 34796 ?? S 9:38PM 0:00.08 nginx: worker process
crazytaxii 48177 0.1 0.1 5397428 35000 ?? S 9:38PM 0:00.08 nginx: worker process
crazytaxii 48185 0.0 0.1 5545908 35048 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48184 0.0 0.1 5517236 34792 ?? S 9:38PM 0:00.08 nginx: worker process
crazytaxii 48183 0.0 0.1 5526452 34944 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48182 0.0 0.1 5273524 35008 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48181 0.0 0.1 5536692 34900 ?? S 9:38PM 0:00.08 nginx: worker process
crazytaxii 48180 0.0 0.1 5525428 35012 ?? S 9:38PM 0:00.08 nginx: worker process
crazytaxii 48179 0.0 0.1 5665716 35008 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48178 0.0 0.1 5546932 34868 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48176 0.0 0.1 5546932 35092 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48174 0.0 0.1 5555124 35040 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48172 0.0 0.1 5666740 35068 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48171 0.0 0.1 5399476 35100 ?? S 9:38PM 0:00.09 nginx: worker process
crazytaxii 48170 0.0 0.1 5678004 35776 ?? S 9:38PM 0:00.10 nginx: worker process
crazytaxii 48169 0.0 0.0 5217568 912 ?? Ss 9:38PM 0:00.01 nginx: master process /usr/local/bin/nginx -p /usr/local/opt/kong -c nginx.conf
这里我们看到 Nginx Master 进程启动时会读取 Kong resty 脚本 在 /usr/local/opt/kong 路径下生成的 nginx.conf 文件。
缓存预热
Kong 为了避免频繁的数据库读写造成严重性能影响会在内存中保持一份缓存,而在 Kong 启动时将完整的数据库读入内存中,也就是所谓的 cache warmup。
对照 OpenResty 锚点图(我说过它会多次出现的),缓存预热操作在 init_worker 阶段执行再合适不过了,这里要注意到上面提到的 Nginx 模板 https://github.com/kong/kong/blob/master/kong/templates/nginx_kong.lua
init_worker_by_lua_block {
Kong.init_worker()
}
在 Nginx 的 Worker 进程启动时首先会执行 init_worker_by_lua_block
锚点中的“预热”代码 https://github.com/kong/kong/blob/master/kong/init.lua:
local function execute_cache_warmup(kong_config)
if kong_config.database == "off" then
return true
end
if ngx.worker.id() == 0 then
local ok, err = cache_warmup.execute(kong_config.db_cache_warmup_entities)
if not ok then
return nil, err
end
end
return true
end
function Kong.init_worker()
ok, err = load_declarative_config(kong.configuration, declarative_entities)
if not ok then
stash_init_worker_error("failed to load declarative config file: " .. err)
return
end
ok, err = execute_cache_warmup(kong.configuration)
if not ok then
ngx_log(ngx_ERR, "failed to warm up the DB cache: " .. err)
end
runloop.init_worker.before()
end
回到 Kong 的配置文件 kong.conf:
#db_cache_warmup_entities = services, plugins
# Entities to be pre-loaded from the datastore
# into the in-memory cache at Kong start-up.
# This speeds up the first access of endpoints
# that use the given entities.
#
# When the `services` entity is configured
# for warmup, the DNS entries for values in
# its `host` attribute are pre-resolved
# asynchronously as well.
#
# Cache size set in `mem_cache_size` should
# be set to a value large enough to hold all
# instances of the specified entities.
# If the size is insufficient, Kong will log
# a warning.
默认将预先加载 services 与 plugins 数据表中的所有数据。
至此我们探索了 kong start -c /path/to/kong.conf
这条启动命令背后的实现原理与过程,下篇我将介绍 Kong 作为 API 网关远见者的理念。