云原生网关 APISIX 的核心流程以源码分析的方式剖析其工作原理-3

举报
苏州程序大白 发表于 2022/04/21 11:54:16 2022/04/21
【摘要】 @[TOC](云原生网关 APISIX 的核心流程以源码分析的方式剖析其工作原理-3) ✨博主介绍🌊 作者主页:苏州程序大白🌊 作者简介:🏆CSDN人工智能域优质创作者🥇,苏州市凯捷智能科技有限公司创始之一,目前合作公司富士康、歌尔等几家新能源公司💬如果文章对你有帮助,欢迎关注、点赞、收藏💅 有任何问题欢迎私信,看到会及时回复💅关注苏州程序大白,分享粉丝福利 主流程以 Ngin...

@[TOC](云原生网关 APISIX 的核心流程以源码分析的方式剖析其工作原理-3)

✨博主介绍

🌊 作者主页:苏州程序大白

🌊 作者简介:🏆CSDN人工智能域优质创作者🥇,苏州市凯捷智能科技有限公司创始之一,目前合作公司富士康、歌尔等几家新能源公司

💬如果文章对你有帮助,欢迎关注、点赞、收藏

💅 有任何问题欢迎私信,看到会及时回复
💅关注苏州程序大白,分享粉丝福利

主流程

以 Nginx HTTP Subsystem 为例分析主要执行逻辑,其中一些核心逻辑已在上述小节中流程分析过。

init_by_lua

在这里插入图片描述

function _M.http_init(args)
    require("resty.core")

    if require("ffi").os == "Linux" then
        require("ngx.re").opt("jit_stack_size", 200 * 1024)
    end

    require("jit.opt").start("minstitch=2", "maxtrace=4000",
                             "maxrecord=8000", "sizemcode=64",
                             "maxmcode=4000", "maxirconst=1000")

    core.resolver.init_resolver(args)
    -- 生成节点 ID
    core.id.init()

    -- 启用 openresty 的特权进程
    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 / yaml 本地配置文件获取配置, etcd 有 init 函数
    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
end

init_worker_by_lua

在这里插入图片描述
在这里插入图片描述

function _M.http_init_worker()
    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)
    -- for testing only
    core.log.info("random test in [1, 10000]: ", math.random(1, 10000))

    -- 进程间事件通信
    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
    -- 服务发现 lib
    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")
    -- TODO admin 流程分析
    require("apisix.admin.init").init_worker()
    -- 注册全局 timer
    require("apisix.timers").init_worker()

    -- 加载所有插件并执行插件 init
    plugin.init_worker()
    -- 初始化 router, 并加载 routes
    router.http_init_worker()

    -- 初始化 services, 加载 services
    require("apisix.http.service").init_worker()
    -- 加载插件配置
    plugin_config.init_worker()
    -- consumer 加载
    require("apisix.consumer").init_worker()

    if core.config == require("apisix.core.config_yaml") then
        core.config.init_worker()
    end

    require("apisix.debug").init_worker()
    -- upstreams 加载
    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

access_by_lua

-- access_by_lua 阶段, apisix 没有 rewrite_by_lua
-- ref: https://github.com/apache/apisix/issues/1120
-- ref: https://github.com/apache/apisix/issues/1120#issuecomment-584949073
function _M.http_access_phase()
    local ngx_ctx = ngx.ctx
	...

    -- 从 table 缓存池中获取 table
    -- always fetch table from the table pool, we don't need a reused api_ctx
    local api_ctx = core.tablepool.fetch("api_ctx", 0, 32)
    -- 将 table 储存在 ngx.ctx 中, 下一个阶段共享
    ngx_ctx.api_ctx = api_ctx

    -- 绑定 metatable 
    core.ctx.set_vars_meta(api_ctx)
	...

    -- router 路由匹配
    router.router_http.match(api_ctx)

    -- run global rule
    plugin.run_global_rules(api_ctx, router.global_rules, nil)

    ...
    local enable_websocket = route.value.enable_websocket

    -- route 插件配置绑定
    if route.value.plugin_config_id then
       	...
        route = plugin_config.merge(route, conf)
    end

    -- 获取对应的 service
    if route.value.service_id then
        local service = service_fetch(route.value.service_id)
        ...
        if enable_websocket == nil then
            enable_websocket = service.value.enable_websocket
        end

    else
        ...
    end
    api_ctx.route_id = route.value.id
    api_ctx.route_name = route.value.name

    -- 执行 script
    if route.value.script then
        script.load(route, api_ctx)
        script.run("access", api_ctx)
    else
        -- 插件过滤, 遍历插件列表, 匹配开启的插件, O(n)
        local plugins = plugin.filter(route)
        api_ctx.plugins = plugins

        -- fake 执行 rewrite 阶段
        plugin.run_plugin("rewrite", plugins, api_ctx)
        if api_ctx.consumer then
            local changed
            route, changed = plugin.merge_consumer_route(
                route,
                api_ctx.consumer,
                api_ctx
            )

            core.log.info("find consumer ", api_ctx.consumer.username,
                          ", config changed: ", changed)

            if changed then
                core.table.clear(api_ctx.plugins)
                api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
            end
        end
        -- 执行 access 阶段
        plugin.run_plugin("access", plugins, api_ctx)
    end

    local up_id = route.value.upstream_id

    -- used for the traffic-split plugin
    if api_ctx.upstream_id then
        up_id = api_ctx.upstream_id
    end
	...

    -- websocket 特殊处理
    if enable_websocket then
        api_ctx.var.upstream_upgrade    = api_ctx.var.http_upgrade
        api_ctx.var.upstream_connection = api_ctx.var.http_connection
        core.log.info("enabled websocket for route: ", route.value.id)
    end

    if route.value.service_protocol == "grpc" then
        api_ctx.upstream_scheme = "grpc"
    end

    -- 获取 upstream 节点
    local code, err = set_upstream(route, api_ctx)
    if code then
        core.log.error("failed to set upstream: ", err)
        core.response.exit(code)
    end

    -- 负载均衡
    local server, err = load_balancer.pick_server(route, api_ctx)
    if not server then
        core.log.error("failed to pick server: ", err)
        return core.response.exit(502)
    end

    api_ctx.picked_server = server

    set_upstream_headers(api_ctx, server)

    -- stash ngx ctx 这部分与 Kong 一致, 怀疑是抄来的(95% 置信区间)
    ngx_var.ctx_ref = ctxdump.stash_ngx_ctx()
    local up_scheme = api_ctx.upstream_scheme
    if up_scheme == "grpcs" or up_scheme == "grpc" then
        return ngx.exec("@grpc_pass")
    end

    if api_ctx.dubbo_proxy_enabled then
        return ngx.exec("@dubbo_pass")
    end
end

一些思考

边缘计算

对于互联网设备,网络边缘是设备或包含设备的本地网络与互联网通信的位置。边缘是个比较模糊的术语。例如,可以将用户的计算机或 IoT 摄像头内部的处理器视为网络边缘,但也可以将用户的路由器、ISP 或本地边缘服务器视为边缘。重要的是,网络边缘在地理位置上靠近设备,与源站和云服务器不同,后者可能与它们相互通信的设备相距很远。

完全减轻额外硬件需求的一种方法是利用边缘服务器。例如,借助 Cloudflare 分散在全球各地的 194 个边缘服务器网络,Cloudflare 的客户可以使用 Cloudflare Workers 在全球范围内运行边缘代码。10

Cloudflare 的边缘计算是基于 Edge Gateway(边缘网关、边缘集群)的 Serverless 代码执行,提供了 JS 代码执行,以及 WASM 二进制。11

一些相关的 Issue:

Serverless

APISIX 的 Serverless 插件功能支持注入任何 Lua 脚本,而 Kong 网关也有类似的插件功能。
在这里插入图片描述
Serverless 插件支持执行简单的函数方法。

WebAssembly

APISIX 自 2019 年发起提案,试图通过 WebAssembly 来扩展 Lua 贫乏的生态。 2021 年,在 WebAssembly 运行时的技术选型上,APISIX 的技术团队更偏向使用由 Fastly 团队 支撑13wasmtime 项目。

开源的 WebAssembly 除了 wasmtime 还有14

  • WasmEdge(前身 SSVM),由 Second State 开源的 CNCF 沙箱项目。
  • Wasmer,Dart 语言使用的 Wasm 运行时。
  • Lucet,由 Fastly 开源的 Bytecode Alliance 的 项目,将会与 wasmtime 合并。

在 Issue #157 的讨论中,Wasmer 的 CEO 也来插了一嘴, 希望 APISIX 能够选型 Wasmer 运行时,APISIX 成员给了 Wasmer 一个大大的赞, 最终在 api7/wasm-nginx-module 插件中, 还是使用 wasmtime 运行时实现了对 WebAssembly 的支持。

Service Mesh

APISIX 的 Service Mesh 项目 api7/apisix-mesh-agent,将 APISIX Proxy 作为 Sidecar 运作在数据平面。通过实现控制平面的接口,接入类似 IstioKuma(由 Kong 创建捐赠给 CNCF) 的控制平面,形成一套完整的 Service Mesh 方案。 该项目本质上是使用 APISIX 替换了 Istio 中的 Envoy。

在这里插入图片描述
值得一提的是 Kong 类似的 Service Mesh 项目,叫做 Kong Mesh,目前只提供企业版本。
在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。