OpenResty工作原理
APISIX本质上就是接管了OpenResty生命周期的框架,所以我们先要了解OpenResty是怎么和lua交互的,下面图片可看到,OpenResty在Nginx的Master和每个Worker进程中加入了LuaJIT VM来解析lua代码,在lua执行过程中创建的协程会共用一个Lua虚拟机。
生命周期
APISIX通过实现以下阶段来达到动态代理用户请求的过程:
init_by_lua
,用于初始化master,只执行一次init_worker_by_lua
,用于初始化worker,只执行一次set_by_lua
,用于设置变量;rewrite_by_lua
,用于转发、重定向等;access_by_lua
,用于准入、权限等;content_by_lua
,用于生成返回内容;header_filter_by_lua
,用于应答头过滤处理;body_filter_by_lua
,用于应答体过滤处理;log_by_lua
,用于日志记录。
变量
在同一worker进程中,Lua代码只会被加载一次,因此在worker内需要共享的只读数据,可通过Lua模块实现。
同个请求跨阶段变量可通过OpenResty的ngx.ctx
或Nginx的set
实现
跨worker共享变量可用shared dict
共享内存实现原子操作
打印信息
从这里开始就不能用前面调试的print输出了,因为虽然代码都在一个地方,但是到这里已经离开APISIX启动脚本,进入Nginx阶段了,所以要用nginx带的ngx.log()
来输出日志信息。
在配置文件error_log_level
字段可以配置日志输出等级,默认是warn
。
在APISIX代码中使用的apisix.core.log
模块来输出日志,也是调用ngx.log
来输出。
init_by_lua_block
lua-nginx-module#init_by_lua_block
init_by_lua_block {
require "resty.core"
# 这里加载的是`/usr/local/apisix/apisix/init.lua`文件,由前面配置的lua_package_path定义的查找路径
apisix = require("apisix")
local dns_resolver = { "127.0.0.11", }
local args = {
dns_resolver = dns_resolver,
}
apisix.http_init(args)
}
复制代码
lua-resty-core/process.md · openresty/lua-resty-core (github.com) 这个库在openresty中已经自带,在/usr/local/openresty/lualib
下
function _M.http_init(args)
-- 初始化dns resolver
core.resolver.init_resolver(args)
-- 初始化id
core.id.init()
-- /usr/local/openresty/lualib/ngx/process.lua
local process = require("ngx.process")
local ok, err = process.enable_privileged_agent()
if not ok then
core.log.error("failed to enable privileged_agent: ", err)
end
-- 请求了一下etcd取值
if core.config.init then
local ok, err = core.config.init()
if not ok then
core.log.error("failed to load the configuration: ", err)
end
end
xrpc.init()
end
复制代码
process.enable_privileged_agent()
会允许Nginx启动一个特权进程,可在容器中用ps aux
看到进程"root 0:00 {openresty} nginx: privileged agent process"
特权进程是OpenResty特有的,特权进程的权限和 master 进程的权限保持一样,在容器中看到这里是root权限,特权进程一般用来处理清理日志、重启OpenResty等需要高权限的任务
init_worker_by_lua_block
这里主要做了一堆初始化的工作
init_worker_by_lua_block {
# 直接执行
apisix.http_init_worker()
}
复制代码
function _M.http_init_worker()
-- 更新一下随机seed
local seed, err = core.utils.get_seed_from_urandom()
if not seed then
core.log.warn('failed to get seed from urandom: ', err)
seed = ngx_now() * 1000 + ngx.worker.pid()
end
math.randomseed(seed)
-- 这里引入了事件
-- /usr/local/apisix/deps/share/lua/5.1/resty/worker/events.lua
local we = require("resty.worker.events")
local ok, err = we.configure({shm = "worker-events", interval = 0.1})
if not ok then
error("failed to init worker event: " .. err)
end
-- 服务发现
local discovery = require("apisix.discovery.init").discovery
if discovery and discovery.init_worker then
discovery.init_worker()
end
require("apisix.balancer").init_worker()
load_balancer = require("apisix.balancer")
-- AdminAPI初始化
-- 路由注册
-- Worker绑定
require("apisix.admin.init").init_worker()
require("apisix.timers").init_worker()
require("apisix.debug").init_worker()
if core.config.init_worker then
local ok, err = core.config.init_worker()
if not ok then
core.log.error("failed to init worker process of ", core.config.type,
" config center, err: ", err)
end
end
-- 插件初始化
plugin.init_worker()
router.http_init_worker()
require("apisix.http.service").init_worker()
plugin_config.init_worker()
require("apisix.consumer").init_worker()
apisix_upstream.init_worker()
require("apisix.plugins.ext-plugin.init").init_worker()
local_conf = core.config.local_conf()
if local_conf.apisix and local_conf.apisix.enable_server_tokens == false then
ver_header = "APISIX"
end
end
复制代码
参考资料
OpenResty 从入门到实战