惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
博客园_首页
量子位
博客园 - 司徒正美
S
SegmentFault 最新的问题
J
Java Code Geeks
小众软件
小众软件
博客园 - 【当耐特】
The Register - Security
The Register - Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
Blog — PlanetScale
Blog — PlanetScale
T
Tailwind CSS Blog
博客园 - Franky
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
Cyberwarzone
Cyberwarzone
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
F
Full Disclosure
D
DataBreaches.Net
Martin Fowler
Martin Fowler
Cisco Talos Blog
Cisco Talos Blog
L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
C
Check Point Blog
T
Threatpost
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
W
WeLiveSecurity
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
Lohrmann on Cybersecurity
Last Week in AI
Last Week in AI
T
Tor Project blog
T
Troy Hunt's Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security Affairs
SecWiki News
SecWiki News

博客园 - 小得盈满

Hudi 时间旅行与增量查询的区别分析 NATS 的基本安装及使用 Doris 和 StarRocks 性能测试对比 ClickHouse 添加磁盘扩容存储 使用 Docker 部署 Elasticsearch 集群 Qdrant 单机和集群的安装方法 ClickHouse UDF 的基本用法 StarRocks 容器镜像构建 Kafka 线上性能调优 StarRocks 集群安装 Kafka 的分片和副本机制 Flink 自定义 ClickHouse Table Connector 的简单实现 Redis 性能优化实战 记一次 Go 调用系统命令出现的问题分析 APISIX proxy-cache 插件用法 etcd 历史版本回溯的方法 Kafka 集群副本数量调整 使用 Docker Compose 安装 APISIX etcd 集群安装 Consul 集群安装
APISIX 简单的自定义插件开发步骤
小得盈满 · 2023-10-31 · via 博客园 - 小得盈满

本文基于 APISIX 3.2 版本进行插件开发并运行通过。

APISIX 目前开发插件比较简单,只需要编写 Lua 源代码并放到默认的插件目录下,然后通过配置文件开启插件即可,我们如果使用 Docker 运行 APISIX 那么默认的安装目录是:/usr/local/apisix,插件目录是:/usr/local/apisix/apisix/plugins ,我们只需要修改这个目录已有的插件源码或者自己编写新的插件放到这个目录,编写完成并启用后,重启 APISIX 将会自动加载。因为使用 Docker 启动,我们可以直接在外部编写插件并且映射到容器内部指定的位置即可。

如果是在第三方的目录编写插件,那么 APISIX 要求插件父目录必须包含:apisix/plugins 子目录,也就是和默认的层级结构一致,比如插件的目录为:/opt/apisix-plugins ,那么实际编写插件位置应该是在:/opt/apisix-plugins/apisix/plugins 。同样是对于容器来说外面并不需要建这么多的层级,映射进去的时候指定就可以,然后需要修改配置文件,添加外部路径:

apisix:
    # ...
    extra_lua_path: "/opt/apisix-plugins/?.lua"

外部路径中相同名称的插件会覆盖现有的插件。

如果选择在默认目录下开发,那么不需要进行上面的配置,开发可以参考内部的 example-plugin.lua 这个插件,这是一个示例代码,默认也启用了,直接复制这个改即可,比如我们创建一个 example-plugin2.lua,代码如下:

--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements.  See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License.  You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local ngx = ngx
local core = require("apisix.core")
local plugin = require("apisix.plugin")

local schema = {
    type = "object",
    properties = {
        name = {type = "string"},
        version = {type = "integer"},
    },
    required = {"name"},
}

local plugin_name = "example-plugin2"

local _M = {
    version = 0.1,
    priority = 99,
    name = plugin_name,
    schema = schema,
}

function get_username()
    local req_headers = ngx.req.get_headers()
    local username = req_headers.user
    if username ~= "" then
        return username
    end
    return nil
end

function _M.check_schema(conf, schema_type)
    if schema_type == core.schema.TYPE_METADATA then
        return core.schema.check(metadata_schema, conf)
    end
    return core.schema.check(schema, conf)
end


function _M.init()
    -- call this function when plugin is loaded
    local attr = plugin.plugin_attr(plugin_name)
    if attr then
        core.log.info(plugin_name, " get plugin attr val: ", attr.val)
    end
end


function _M.destroy()
    -- call this function when plugin is unloaded
end

function _M.rewrite(conf, ctx)
    core.log.warn("plugin rewrite phase, conf: ", core.json.encode(conf))
    core.log.warn("plugin rewrite phase, ctx: ", core.json.encode(ctx, true))
    core.log.warn("plugin rewrite phase, username: ", get_username())
end


function _M.access(conf, ctx)
    core.log.warn("plugin access phase, conf: ", core.json.encode(conf))
    core.log.warn("plugin access phase, ctx: ", core.json.encode(ctx, true))
    core.log.warn("plugin access phase, ngx headers: ", core.json.encode(ngx.req.get_headers()))
end

local function hello()
    local args = ngx.req.get_uri_args()
    if args["json"] then
        return 200, {msg = "world"}
    else
        return 200, "world\n"
    end
end


function _M.control_api()
    return {
        {
            methods = {"GET"},
            uris = {"/v1/plugin/example-plugin2/hello"},
            handler = hello,
        }
    }
end

return _M

这就是一个比较简单的插件,其中没设置元数据,其余的部分简单说下:

  1. schema 表示插件的配置需要在启用时指定
  2. _M 表示一个插件实例,可以指定名称、版本、优先级、schema 等,优先级建议设置 0~ 100 之间的值。
  3. 然后我们就可以给 _M 绑定不同的方法,按照请求阶段分别为:init、check_schema、rewrite、access、before_proxy、header_filter、body_filter、log 等阶段,我们需要选择适合的阶段进行处理。另外还有 control_api 方法,由插件提供并定义响应结果,可以监控插件的一些信息等。

我们上面这个插件就是简单打印了一些信息,编写了一个函数获取 Header 里面的用户,然后我们需要复制 config-default.yaml 配置文件中的 plugins 配置项并填写到 config.yaml 中,因为之前的插件都要保留着不要给覆盖掉了,具体 config.yaml 中的内容如下:

plugins:                           # plugin list (sorted by priority)
  - real-ip                        # priority: 23000
  - ai                             # priority: 22900
  - client-control                 # priority: 22000
  - proxy-control                  # priority: 21990
  - request-id                     # priority: 12015
  - zipkin                         # priority: 12011
  #- skywalking                    # priority: 12010
  #- opentelemetry                 # priority: 12009
  - ext-plugin-pre-req             # priority: 12000
  - fault-injection                # priority: 11000
  - mocking                        # priority: 10900
  - serverless-pre-function        # priority: 10000
  #- batch-requests                # priority: 4010
  - cors                           # priority: 4000
  - ip-restriction                 # priority: 3000
  - ua-restriction                 # priority: 2999
  - referer-restriction            # priority: 2990
  - csrf                           # priority: 2980
  - uri-blocker                    # priority: 2900
  - request-validation             # priority: 2800
  - chaitin-waf                    # priority: 2700
  - openid-connect                 # priority: 2599
  - cas-auth                       # priority: 2597
  - authz-casbin                   # priority: 2560
  - authz-casdoor                  # priority: 2559
  - wolf-rbac                      # priority: 2555
  - ldap-auth                      # priority: 2540
  - hmac-auth                      # priority: 2530
  - basic-auth                     # priority: 2520
  - jwt-auth                       # priority: 2510
  - key-auth                       # priority: 2500
  - consumer-restriction           # priority: 2400
  - forward-auth                   # priority: 2002
  - opa                            # priority: 2001
  - authz-keycloak                 # priority: 2000
  #- error-log-logger              # priority: 1091
  - proxy-cache                    # priority: 1085
  - body-transformer               # priority: 1080
  - proxy-mirror                   # priority: 1010
  - proxy-rewrite                  # priority: 1008
  - workflow                       # priority: 1006
  - api-breaker                    # priority: 1005
  - limit-conn                     # priority: 1003
  - limit-count                    # priority: 1002
  - limit-req                      # priority: 1001
  #- node-status                   # priority: 1000
  - gzip                           # priority: 995
  - server-info                    # priority: 990
  - traffic-split                  # priority: 966
  - redirect                       # priority: 900
  - response-rewrite               # priority: 899
  - degraphql                      # priority: 509
  - kafka-proxy                    # priority: 508
  #- dubbo-proxy                   # priority: 507
  - grpc-transcode                 # priority: 506
  - grpc-web                       # priority: 505
  - public-api                     # priority: 501
  - prometheus                     # priority: 500
  - datadog                        # priority: 495
  - loki-logger                    # priority: 414
  - elasticsearch-logger           # priority: 413
  - echo                           # priority: 412
  - loggly                         # priority: 411
  - http-logger                    # priority: 410
  - splunk-hec-logging             # priority: 409
  - skywalking-logger              # priority: 408
  - google-cloud-logging           # priority: 407
  - sls-logger                     # priority: 406
  - tcp-logger                     # priority: 405
  - kafka-logger                   # priority: 403
  - rocketmq-logger                # priority: 402
  - syslog                         # priority: 401
  - udp-logger                     # priority: 400
  - file-logger                    # priority: 399
  - clickhouse-logger              # priority: 398
  - tencent-cloud-cls              # priority: 397
  - inspect                        # priority: 200
  #- log-rotate                    # priority: 100
  # <- recommend to use priority (0, 100) for your custom plugins
  - example-plugin                 # priority: 0
  #- gm                            # priority: -43
  - aws-lambda                     # priority: -1899
  - azure-functions                # priority: -1900
  - openwhisk                      # priority: -1901
  - openfunction                   # priority: -1902
  - serverless-post-function       # priority: -2000
  - ext-plugin-post-req            # priority: -3000
  - ext-plugin-post-resp           # priority: -4000
  - example-plugin2                # priority: 99

上面最后一项就是我们自定义的插件,然后如果是使用 Docker Compose 启动,插件可以映射如下:

version: "3"

services:
  apisix:
    # ...
    volumes:
      - ./conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
      # 自定义插件映射
      - ./plugins/example-plugin2.lua:/usr/local/apisix/apisix/plugins/example-plugin2.lua

然后我们重启启动 APISIX,启动没有报错可以尝试添加路由:

curl -XPUT 127.0.0.1:9180/apisix/admin/routes/example2 -H 'X-API-KEY: <admin-key>' -d '
{
	"plugins": {
	    "example-plugin2": {
	        "name": "example2"
	    }
	},
	"upstream": {
	    "type": "roundrobin",
	    "nodes": {
            "127.0.0.1:1980": 1
        }
	},
	"uri": "/hello"
}'

然后我们访问数据平面服务即可:

curl -XGET 127.0.0.1:9080/hello

这时候我们看 APISIX 的日志就可以看到我们插件打印出的详细信息了,基于上面的插件开发运行的原理可以根据需要编写更多的代码来扩展功能。

Reference:

  1. https://apisix.apache.org/docs/apisix/3.2/plugin-develop/
  2. https://www.cnblogs.com/loseself/p/16151876.html