悟 Kong(那美克星篇)

May 2, 2020 22:45 · 1872 words · 4 minute read Gateway Nginx OpenResty Kong

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 的 startstop 命令来启动或者停止 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 在启动时所有的步骤:

  1. 加载并解析配置文件
  2. 断言是否有 Nginx(OpenResty) 进程在运行
  3. 连接 PostgreSQL 或者 Cassandra 数据库
  4. 通过源码中内置的模板渲染出一份 nginx.conf 配置文件
  5. 检查数据表是否最新
  6. 启动 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 网关远见者的理念。