乐趣区

关于apisix:Apache-APISIX-扩展指南

Apache APISIX 提供了 50 多个插件、罕用的几个负载平衡选择器,以及对支流服务发现(如 Nacos 和 DNS)的反对。API 网关和企业外部的业务严密相干,为了满足企业的业务需要,用户通常须要在 Apache APISIX 的根底上增加一些代码,以实现业务所需的性能。如何拓展 Apache APISIX 成了许多用户的独特痛点:在保障 Apache APISIX 安稳运行的前提下,如何增加业务代码,满足理论需要?

本文提供了 Apache APISIX 的拓展指南,旨在为用户提供拓展 Apache APISIX 的一些思路。因为 Apache APISIX 处于高速倒退阶段,版本迭代的频率比拟高,所以本文会以 Apache APISIX 的首个 LTS 版本 v2.10.0 为根底进行阐明。如果你应用的 Apache APISIX 版本低于 2.10.0,可能须要结合实际状况做一些变通。另外,尽管本文只解说了 HTTP 相干的逻辑,然而 TCP 相干的局部,大体上也较为类似。

拓展方向 1:Rewrite 还是 Access?

咱们从申请的生命周期说起:当一个申请进入到 Apache APISIX 时,首先会由 http_access_phase 这个办法进行解决。相熟 OpenResty phases 概念的读者可能会有些纳闷:OpenResty 一共有 6 个 phases,依照执行的先后顺序排列,别离是:rewriteaccessbefore_proxyheader_filterbody_filterlog,为什么一上来就是 accessrewrite 怎么不见了?

Apache APISIX 插件的 phases 概念和 OpenResty 的 phases 概念略有不同。为了晋升 Apache APISIX 的性能,APISIX 插件的 rewrite 办法会在 OpenResty 的 access 阶段中运行。用户仍然可能在插件层面自定义 rewrite 的逻辑,然而在代码层面,rewrite 实际上也是在 access 外面执行。

尽管说 rewrite 的逻辑和 access 的逻辑都在 access phase 外面运行,然而 rewrite 的逻辑还是会比 access 的逻辑先执行。为了防止后续插件执行 rewrite 失败后,没有继续执行 access,导致 trace 脱漏的问题,必须在rewrite 外面增加 trace 的逻辑。

除了执行的先后顺序外,rewriteaccess 之间还有一个差异,就是它们之间有一个解决 consumer 的逻辑:

 plugin.run_plugin("rewrite", plugins, api_ctx)
        if api_ctx.consumer then
            ...
        end
        plugin.run_plugin("access", plugins, api_ctx)

consumer 代表一种身份。你能够针对不同的 consumer 进行权限管制,比方应用 consumer-restriction 这个插件实现基于角色的权限管制,也就是大家所说的 RBAC。另外,你也能够给不同的 consumer 设置对应的限流策略。

Apache APISIX 外面的鉴权插件(在插件定义外面有 type = "auth"),须要在 rewrite 阶段选好 consumer。这里咱们用 key-auth 插件举个例子:

local _M = {
    version = 0.1,
    priority = 2500,
    type = 'auth',
    name = plugin_name,
    schema = schema,
    consumer_schema = consumer_schema,
}

...
function _M.rewrite(conf, ctx)
    ...
    local consumer_conf = consumer_mod.plugin(plugin_name)
    if not consumer_conf then
        return 401, {message = "Missing related consumer"}
    end

    local consumers = lrucache("consumers_key", consumer_conf.conf_version,
        create_consume_cache, consumer_conf)

    local consumer = consumers[key]
    if not consumer then
        return 401, {message = "Invalid API key in request"}
    end

    consumer_mod.attach_consumer(ctx, consumer, consumer_conf)
end

鉴权插件的执行逻辑都是类似的:首先从用户输出中获取某组参数,而后依据参数查找对应的 consumer,最初连同该插件对应的 consumer_conf 附加到 ctx 中。

综上,对于无需在申请晚期阶段执行,且不须要查找 consumer 的插件,倡议把逻辑写到 access 外面。

拓展方向 2:配置服务发现

在执行完 access 之后,咱们就要跟上游(Upstream)打交道了。通常状况下,上游节点是写死在 Upstream 配置上的。不过也能够从服务发现上获取节点来实现 discovery。

接下来咱们会以 Nacos 为例,讲讲怎么实现。

一个动静获取 Nacos 治理的节点的 Upstream 配置如下:

{
    "service_name": "APISIX-NACOS",
    "type": "roundrobin",
    "discovery_type": "nacos",
    "discovery_args": {
        "namespace_id": "test_ns",
        "group_name": "test_group"
    }
}

咱们能够看到其中三个重要的变量:

  1. discovery_type: 服务发现的类型,"discovery_type": "nacos"示意应用 Nacos 实现服务发现。
  2. service_name: 服务名称。
  3. discovery_args: 不同的 discovery 特定的参数,Nacos 的特定参数包含:namespace_idgroup_name

Nacos discovery 对应的 Lua 代码位于 discovery/nacos.lua。关上 nacos.lua 这个文件,咱们能够看到它外面实现了几个所需的办法。

一个 discovery 须要实现至多两个办法:nodesinit_worker

function _M.nodes(service_name, discovery_args)
    local namespace_id = discovery_args and
            discovery_args.namespace_id or default_namespace_id
    local group_name = discovery_args
            and discovery_args.group_name or default_group_name

    ...
end


function _M.init_worker()
    ...
end

其中nodes 的函数签名曾经明了地展现获取新节点所用的查问参数:service_namediscovery_args。每次申请时,Apache APISIX 都会用这一组查问最新的节点。该办法返回的是一个数组:

{{host = "xxx", port = 12100, weight = 100, priority = 0, metadata = ...}, 
    # priority 和 metadata 是可选的
    ...
}

init_worker 负责在后盾启动一个 timer,确保本地的节点数据能跟服务发现的数据保持一致。

拓展方向 3:配置负载平衡

获取到一组节点后,咱们要依照负载平衡的规定来决定接下来要先尝试哪个节点。如果罕用的几种负载平衡算法满足不了需要,你也能够本人实现一个负载平衡。

让咱们以起码连接数负载平衡为例。对应的 Lua 代码位于 balancer/least_conn.lua。关上least_conn.lua 这个文件,咱们能够看到它外面实现了几个所需的办法:newgetafter_balancebefore_retry_next_priority

  • new 负责做一些初始化工作。
  • get 负责执行选中节点的逻辑。
  • after_balance 在上面两种状况下会运行:
  • 每次重试之前(此时 before\_retry 为 true)
  • 最初一次尝试之后
  • before_retry_next_priority 则是在每次尝试完以后一组同 priority 的节点,筹备尝试下一组之前运行。
function _M.new(up_nodes, upstream)
    ...

    return {
        upstream = upstream,
        get = function (ctx)
            ...
        end,
        after_balance = function (ctx, before_retry)
            ...
            if not before_retry then
                if ctx.balancer_tried_servers then
                    core.tablepool.release("balancer_tried_servers", ctx.balancer_tried_servers)
                    ctx.balancer_tried_servers = nil
                end

                return nil
            end

            if not ctx.balancer_tried_servers then
                ctx.balancer_tried_servers = core.tablepool.fetch("balancer_tried_servers", 0, 2)
            end

            ctx.balancer_tried_servers[server] = true
        end,
        before_retry_next_priority = function (ctx)
            if ctx.balancer_tried_servers then
                core.tablepool.release("balancer_tried_servers", ctx.balancer_tried_servers)
                ctx.balancer_tried_servers = nil
            end
        end,
    }
end

如果没有外部状态须要保护,能够间接借用固定的模板代码(上述代码中,位于省略号以外的内容)来填充 after_balancebefore_retry_next_priority 这两个办法。

选中节点之后,咱们也能够通过插件的模式增加额定的逻辑。插件能够实现 before_proxy 办法。该办法会在选中节点之后调用,咱们能够在该办法外面记录以后选中的节点信息,这在 trace 外面会有用。

拓展方向 4:解决响应

咱们能够通过 response-rewrite 插件,在 header_filterbody_filter 解决上游返回的响应。前一个办法是批改响应头,后一个办法批改响应体。留神 Apache APISIX 的响应解决是流式的,如果header_filter 外面没有批改响应头,响应头就会被先发送进来,到了body_filter 就没方法批改响应体了。

这意味着如果你后续想要批改 body,然而 header 外面又有 Content-Length 之类跟 body 相干的响应头,那么就要提前在 header_filter 外面把这些头改掉。咱们提供了一个辅助办法:core.response.clear_header_as_body_modified,只须要在 header_filter 调用它就行。

body_filter 也是流式的,而且还会被屡次调用。所以如果你想要获取残缺的响应体,你须要把每次 body_filter 提供的局部响应体给拼起来。在 Apache APISIX master 分支上,咱们提供了一个叫做 core.response.hold_body_chunk 的办法来简化操作,感兴趣的读者能够看看代码。

拓展方向 5:上报日志和监控参数

在申请完结之后,咱们还能够通过 log 办法来做一些清场工作。这一类工作能够分成两类:

  1. 记录 metrics 指标,比方 prometheus 插件。
  2. 记录 access log,而后定期上报,比方 http-logger 插件。

如果你感兴趣的话,能够看看这两个插件的 log 办法是怎么实现的:

  • prometheus 插件文档:https://apisix.apache.org/zh/…
  • http-logger 插件文档:https://apisix.apache.org/zh/…

对于 Apache APISIX

Apache APISIX 是一个动静、实时、高性能的开源 API 网关,提供负载平衡、动静上游、灰度公布、服务熔断、身份认证、可观测性等丰盛的流量治理性能。Apache APISIX 能够帮忙企业疾速、平安地解决 API 和微服务流量,包含网关、Kubernetes Ingress 和服务网格等。

Apache APISIX 落地用户(仅局部)

  • Apache APISIX GitHub:https://github.com/apache/apisix
  • Apache APISIX 官网:https://apisix.apache.org/
  • Apache APISIX 文档:https://apisix.apache.org/zh/…
退出移动版