本文基于 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

APISIX 简单的自定义插件开发步骤的更多相关文章

  1. discuz特殊主题插件开发步骤和犯的愚蠢错误

    discuz作为国内流行的论坛系统,可谓造福了不少趣味相投的网友们.它让天南地北.国内外有着共同兴趣爱好的人们聚集在一起,分享彼此的喜怒哀乐.心得体会.然而作为discuz的使用者之一,还是个码农,然 ...

  2. Android自定义view(一):制作一个最最最简单的自定义view

    转载:https://blog.csdn.net/wsyizmao/article/details/78491422 浅谈安卓自定义view(一):制作一个最最最简单的自定义view 对于安卓程序员来 ...

  3. Cordova自定义插件开发

    Cordova自定义插件开发 一.创建Cordova项目 在创建项目前请确保安装Cordova Cordova环境配置:https://www.w3cschool.cn/cordova/cordova ...

  4. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

  5. 最简单的自定义适配器adapter

    下面是一个非常简单的自定义适配器的总体源码,从这个源码入门,就可以慢慢学会适配器了 适配器的作用: 完成数据和界面控件的绑定,把数据绑定到界面的现实控件条目上(对于listView,应该是listVi ...

  6. 一个简单的jQuery插件开发实例

    两年前写的一个简单的jQuery插件开发实例,还是可以看看的: <script type="text/javascript" src="jquery-1.7.2.m ...

  7. java:jsp: 一个简单的自定义标签 tld

    java:jsp: 一个简单的自定义标签 tld 请注意,uri都是:http://www.tag.com/mytag,保持统一,要不然报错,不能访问 tld文件 <?xml version=& ...

  8. js/jq基础(日常整理记录)-4-一个简单的自定义tree插件

    一.一个简单的自定义tree插件 上一篇是之前自定义的table插件,这一篇也是之前同期尝试做的一个tree插件. 话不多说,先看看长什么样子哈! 现在来看确实不好看,以后在优化吧! 数据源:ajax ...

  9. wxwidget自定义消息处理步骤

    from http://www.cppblog.com/kenlistian/archive/2009/02/06/73096.html 略有修改 wxwidget自定义消息处理步骤 自定义消息处理( ...

  10. 简单的JDBC编程步骤

    1.加载数据库驱动(com.mysql.jdbc.Driver) 2.创建并获取数据库链接(Connection) 3.创建jdbc statement对象(PreparedStatement) 4. ...

随机推荐

  1. WAF和IPS的区别

    简介 Web应用防火墙WAF(Web Application Firewall)和入侵防御系统IPS(Intrusion Prevention System)是网络安全领域中常见的两种安全解决方案,它 ...

  2. Navicat Premium15 解决只能显示前1000条记录问题

    Navicat Premium15 解决只能显示前1000条记录问题 最近使用Navicat Premium15图形化界面操作MySQL的数据库,发现在超过1461条记录的表里,只能显示前1000条, ...

  3. 【LLM应用】基于GPT3.5的代码编辑器Cursor试用-智能代码编辑助手

    一.安装和使用 官网下载安装Cursor,然后创建一个代码文件.Ctrl +  K生成, Ctrl + L提问. 默认每小时30词. 1. Try generating with command K ...

  4. [noip2015]运输计划(LCA,二分)

    运输计划[做题笔记] 挺难绷的... 题意 概括:给定 \(n\) 个节点的树和 \(n-1\) 条边的权值,现在可以将一条边的权值改为 \(0\) .找出一条边,使得将这条边权值赋为 \(0\) 时 ...

  5. 使用现代身份验证(OAuth)来连接POP、IMAP或SMTP

    我的博客园:https://www.cnblogs.com/CQman/ 转载: https://mp.weixin.qq.com/s?__biz=MzU0MzUxMzU2NA==&mid=2 ...

  6. 虚拟DOM的理解与总结

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. 对虚拟DOM的理解? 从本质上来说,Virtual Dom是一个JavaScript对象,通过对象的方式来表示DOM结构.将页面的状 ...

  7. iOS Modern Collection View

    TL;DR 使用的技术: Compositional layout + Diffable data source.iOS 14+. 创建 layout 以描述布局: 创建 dataSource 以提供 ...

  8. 基于proteus的4026的二分频计数

    基于proteus的4026的二分频计数 1.芯片原理 4026还是一个CMOS芯片,是直接输出段码的计数器.显然,这个芯片的作用就是和七段数码管配合,直接将计数结果显示在数码管上.这里只是用于分频, ...

  9. CentripetalNet:更合理的角点匹配,多方面改进CornerNet | CVPR 2020

    CentripetalNet的核心在于新的角点匹配方式,额外学习一个向心偏移值,偏移值足够小的角点即为匹配,相对于embedding向量的匹配方式,这种方法更为鲁棒,解释性更好.另外论文提出的十字星变 ...

  10. KingbaseES 行级安全策略介绍

    本文详细介绍了KingbaseES中通过CREATE POLICY为一个表定义一条行级安全性策略.注意为了应用已被创建的策略,在表上必须启用行级安全性. 策略名称是针对每个表的.因此,一个策略名称可以 ...