【APISIX源码探究】与openresty交互

OpenResty工作原理

APISIX本质上就是接管了OpenResty生命周期的框架,所以我们先要了解OpenResty是怎么和lua交互的,下面图片可看到,OpenResty在Nginx的Master和每个Worker进程中加入了LuaJIT VM来解析lua代码,在lua执行过程中创建的协程会共用一个Lua虚拟机。 jit.png

生命周期

APISIX通过实现以下阶段来达到动态代理用户请求的过程: openresty lua加载生命周期

  • 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 从入门到实战

分类:
后端