介绍
在Kong 2.0之前,Lua是唯一支持编写Kong插件的语言,并且仍然是开发和扩展Kong的主要方法。Go插件支持的新增功能使Kong用户可以进入Go生态系统。例如,有些数据库(例如MS SQL Server)没有很好的Lua客户端库,但Go很好地支持了这些数据库。Go插件可以直接访问此类服务器,而无需传递Kong的Lua代码。
结构
Go PDK完全基于用Go编写的单独过程。您可以命名一个go-pluginserver作为Kong的启动进程,并打开一个通讯通道以在它们之间传递事件和函数调用。这意味着Go插件可以在真实的Go环境中运行,并且可以使用Go功能,例如goroutine,I / O,IPC等。
注意:这也意味着对PDK函数的任何调用都必须转移到Kong流程中,然后再返回。
Go插件是使用-buildmode=plugin
标志编译的,该标志允许插件服务器动态加载它们。为了遵守Go严格的链接兼容性检查,必须使用kong/go-plugin-tool
Docker镜像对其进行编译,如下所述。
前置条件
使用Go插件:
- 您需要可执行文件如
go-pluginserver
。将其安装在中/usr/local/bin/
。如果要在其他任何地方使用,请将go_pluginserver_exe变量名设置在
Kong配置文件中并且值是他的完整路径。 - 将
go_plugins_dir
Kong配置文件中的变量设置为有效目录。默认"off"
值禁用Go支持。 - 将已编译的Go插件添加到上一步中指定的目录中。
- 通过管理API,数据库或声明性文件以通常方式添加插件设置。通过其文件名(不带
.so
后缀)引用插件。
编写自己的Go插件:
- 具有Kong开发环境。
-
拥有kong/go-plugin-tool:<version> docker镜像
,<version>
应该是与kong相同的版本。
发展历程
环境一致性约束
Golang开发以较低的入门障碍和易于部署而闻名。即使用Go编写的复杂程序也可以作为单个可执行文件分发,您可以将其复制到任何地方并直接运行。
为了实现这一点,编译器默认情况下会生成静态链接的可执行文件。这种选择的一个重大缺点是,这使得扩展“完成的” Go程序非常困难。有几种方法可以解决此限制,但是大多数方法都涉及某种形式的进程间通信。由于语言和基本库都对此提供了很好的支持,因此通常这是一个很好的解决方案,但并非总是如此。
在其他语言中,为Kong选择的扩展策略很常见:插件是动态加载的模块。为此,可执行文件和插件依赖于系统库,而不是生成完全静态的程序。
这是Golang中相对较新的功能,在工具和可部署性方面有一些粗略的优势。特别是,加载的可执行文件(go-pluginserver
在我们的例子中)和插件必须具有完全相同的链接行为,这一点至关重要。这至少涉及:
- 任何常用库的相同版本,包括:
Kong/go-pdk
- 所有的标准库(比如
fmt
,rpc
,reflect
,等) - OS的库,例如
libpthread
,libc
,ld-xxxx
等。
- 与Go编译器完全相同的版本。
- 相同的Go环境变量,例如
$GOROOT
和$GOPATH
公共库版本兼容性由go.mod
依赖项管理部分处理,但这会在环境变量要求方面引入更复杂的问题。
例如,evironment变量$GOPATH
是一个现实问题,不仅因为推荐的模式之一是$HOME/go
,其中包括开发人员在其自己系统中的用户名,而且还因为生产构建(Dockerfile,构建脚本,CI / CD系统)很常见。使用非常不同的模式。
为了保证一致性,kong/go-plugin-tool
用作Go编译器的包装。Kong发行软件包和映像使用它来编译包含的软件包go-pluginserver
。
开发过程
要在Go中编写Kong插件,您需要:
- 定义结构类型以保存配置。
- 编写一个
New()
函数来创建您的结构实例。 - 在该结构上添加方法以处理事件。
- 使用编译
docker run --rm -v $(pwd):/plugins kong/go-plugin-tool:<version> build <source>
。 - 将生成的库(
.so
文件)放入go_plugins_dir
目录中。
注意:这里是个demo,请查看https://github.com/Kong/go-plugins 。
配置结构
用Lua编写的插件定义了一个架构,用来指定如何读取和验证来自数据存储区或Admin API的配置数据。由于Go是一种静态类型语言,因此所有规范都是通过定义结构体来处理的。
type MyConfig struct {
Path string
Reopen bool
}
公共字段(即以大写字母开头的字段)将填充配置数据。如果希望它们在数据存储区中使用其他名称,请添加在encoding/json
包装上定义的字段标签:
type MyConfig struct {
Path string `json:my_file_path`
Reopen bool `json:reopen`
}
New()
初始化
您的插件必须定义一个名为New的函数,该函数创建此类型的实例并以形式返回interface{}
。在大多数情况下,就是这样:
func New() interface{} {
return &MyConfig{}
}
您可以向结构中添加更多字段,这些字段将被传递,但是不能保证配置实例的寿命或数量。
处理事件
要处理Kong事件,请在您的配置结构中定义相关方法。例如,要处理“访问”事件,请定义如下函数:
func (conf *MyConfig) Access (kong *pdk.PDK) {
…
}
你可以定义事件的方法是Certificate
,Rewrite
,Access
,Preread
和 Log
。它们的签名都是相同的。
go-pdk
包
添加"github.com/Kong/go-pdk"
到导入的程序包。kong
事件处理程序方法上收到的指针是Go PDK函数的入口点。这些功能大多数都与Lua PDK中的相应功能相同。
有关Go PDK的参考文档,请参见go-pdk godoc页面。