标签归档:Openresty

在 lua 中使用 protobuf

安装protobuf

具体步骤可以参考官方文档https://github.com/protocolbuffers/protobuf/blob/master/src/README.md

这里只列出一些其中比较的重要的步骤。

先安装一些依赖的库

sudo apt-get install autoconf automake libtool curl make g++ unzip

然后下载需要的安装包

curl -L -o protobuf-all-3.6.1.tar.gz  https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz

接着解压

tar -xzvf protobuf-all-3.6.1.tar.gz

解压完成后进目录

cd protobuf-all-3.6.1

执行安装命令

./configure
make
make check
sudo make install
sudo ldconfig

最后检查是否安装成功

$ protoc --version
libprotoc 3.6.1

安装lua-protobuf

lua-protobuf 实际上是一个纯C的protobuf协议实现,和对应的Lua绑定。

项目地址:https://github.com/starwing/lua-protobuf

可以使用 luarocks 安装lua-protobuf

luarocks install lua-protobuf

如果没有安装 luarocks 可以安装一下luarocks。

使用prortobuf

首先,需要定义你自己的标准proto文件 xxx.proto 文件(可能不止一个文件,不止一个目录),定义好了之后,使用protoc生成pb文件,命令如下:

protoc --proto_path=proto --descriptor_set_out=common.pb proto/xxxx/*.proto proto/xxxxx/*.proto

这里需要注意的是,有时候会有多个proto文件并且多目录import的情况,这个时候,就需要在参数中都体现出来(命令的最后两个参数),这条命令是把所有的 .proto 文件生成了一个 common.pb 文件方便引入。

最后就可以在lua代码中如下使用

-- 引入pb库
local pb = require "pb"

-- load pb文件
assert(pb.loadfile "common.pb" )

-- lua table data
local data = {
    aaa = 123456,
    bbb = "bbbbb"
}

-- 把一个lua table数据encode成二进制文件
local bytes = assert(pb.encode("dd01.account", data))
print(pb.tohex(bytes))

-- 把一个二进制文件decode为lua table
local data2 = assert(pb.decode("dd01.account", bytes))
print(cjson_encode(data2))

如果能看到正常打印出来,说明就成功了。

参考链接

微服务 API 网关 Kong 插件开发 – 插件配置

原文地址:https://docs.konghq.com/1.1.x/plugin-development/plugin-configuration/(不能保证所有的翻译都是准确无误的,所有如有翻译的不准确或错误之处,请一定记得查看原文,并欢迎留言指出)。

简介

大多数情况下,您的插件可以配置为满足您的所有用户需求。当插件被执行的时候,您的插件的配置存储在Kong的数据存储区中,以检索它并将其传递给handler.lua方法。

配置由Kong中的Lua表组成,我们称之为 schema。它包含用户在通过Admin API启用插件时将设置的键/值属性。Kong为您提供了一种验证用户插件配置的方法。

当用户向Admin API发出请求以启用或更新给定Service,Route和/或Consumer上的插件时,将根据您的架构schema插件的配置。

例如,用户执行以下请求:

$ curl -X POST http://kong:8001/services/<service-name-or-id>/plugins/ \
    -d "name=my-custom-plugin" \
    -d "config.foo=bar"

如果配置对象的所有config都根据您的模式有效,则API将返回201 Created,并且插件将与其配置一起存储在数据库中(在这种情况下为{foo =“bar”})。如果配置无效,Admin API将返回400 Bad Request和相应的错误消息。

模块

kong.plugins.<plugin_name>.schema

schema.lua规范

此模块将返回一个Lua表,其中包含将定义用户以后如何配置插件的属性的属性。 可用的属性是:

属性名称 Lua type 默认值 描述
no_consumer Boolen false 如果为true,则无法将此插件应用于特定的Consumer。此插件必须仅应用于服务和路由。例如:身份验证插件。
fields Table {} 你插件的schema,可用属性及其规则的键/值表。
self_check Function nil 如果要在接受插件配置之前执行任何自定义验证,则要实现的功能。

self_check函数必须按如下方式实现:

-- @param `schema` 描述插件配置的架构(规则)的表。
-- @param `config` 当前插件配置的键/值表。
-- @param `dao` DAO的一个实例 (查看 DAO 章节).
-- @param `is_updating` 一个布尔值,指示是否在更新的上下文中执行此检查。
-- @return `valid` 一个布尔值,指示插件的配置是否有效。
-- @return `error` 一个 DAO 错误 (查看 DAO 章节)

以下是一个可能的schema.lua文件的示例:

return {
  no_consumer = true, -- 此插件仅适用于服务或路由,
  fields = {
    -- 在此处描述您的插件配置架构。
  },
  self_check = function(schema, plugin_t, dao, is_updating)
    -- 执行任何自定义验证
    return true
  end
}

描述您的配置schema

schema.lua文件的fields自选描述了插件配置的schema。它是一个灵活的键/值表,其中每个键都是插件的有效配置属性,每个键都是一个描述该属性规则的表。例如:

 fields = {
    some_string = {type = "string", required = true},
    some_boolean = {type = "boolean", default = false},
    some_array = {type = "array", enum = {"GET", "POST", "PUT", "DELETE"}}
  }

以下是属性的规则列表:

规则 LUA TYPE(S) 可使用的值 描述
type string “id”, “number”, “boolean”, “string”, 
“table”, “array”, “url”, “timestamp”
验证属性的类型。
required boolean 默认值:false。
如果为true,则该属性必须存在于配置中。
unique boolean 默认值:false。
如果为true,则该值必须是唯一的(请参阅下面的注释)。
default any 如果未在配置中指定该属性,则将该属性设置为给定值。
immutable boolean 默认值:false。
如果为true,则在创建插件配置后将不允许更新该属性。
enum table 属性的可接受值列表。不接受此列表中未包含的任何值。
regex string 用于验证属性值的正则表达式。
schema table 如果属性的类型是table,则定义用于验证这些子属性的模式。
func function 用于对属性执行任何自定义验证的函数。请参阅后面的示例,了解其参数和返回值。
  • type:将转换从请求参数中检索的值。如果类型不是本机Lua类型之一,则会对其执行自定义验证:
    • id:必须是string
    • timestamp:必须是nember
    • uri:必须是有效的URL
    • array:必须是整数索引表(相当于Lua中的数组)。在Admin API中,可以通过在请求的正文中使用不同值的属性键的多次来发送这样的数组,或者通过单个body参数以逗号分隔。
  • unique:此属性对插件配置没有意义,但在插件需要在数据存储区中存储自定义实体时使用。
  • schema:如果您需要对嵌套属性进行深化验证,则此字段允许您创建嵌套模式。模式验证是递归的。任何级别的嵌套都是有效的,但请记住,这会影响插件的可用性。
  • 附加到配置对象但schema中不存在的任何属性也将使所述配置无效。

例子

key-auth插件的schema.lua文件定义了API密钥的可接受参数名称的默认列表,以及默认设置为false的布尔值:

-- schema.lua
return {
  no_consumer = true,
  fields = {
    key_names = {type = "array", required = true, default = {"apikey"}},
    hide_credentials = {type = "boolean", default = false}
  }
}

于是,当在handler.lua中实现插件的access()函数并且用户使用默认值启用插件时,您可以如下:

-- handler.lua
local BasePlugin = require "kong.plugins.base_plugin"
local CustomHandler = BasePlugin:extend()

function CustomHandler:new()
  CustomHandler.super.new(self, "my-custom-plugin")
end

function CustomHandler:access(config)
  CustomHandler.super.access(self)

  kong.log.inspect(config.key_names)        -- {"apikey"}
  kong.log.inspect(config.hide_credentials) -- false
end

return CustomHandler

请注意,上面的示例使用插件开发工具包(PDK)kong.log.inspect函数将这些值打印到Kong日志中。

一个更复杂的示例,可用于最终日志记录插件:

-- schema.lua

local function server_port(given_value, given_config)
  -- 自定义验证
  if given_value > 65534 then
    return false, "port value too high"
  end

  -- 如果环境是“开发”,8080将是默认端口
  if given_config.environment == "development" then
    return true, nil, {port = 8080}
  end
end

return {
  fields = {
    environment = {type = "string", required = true, enum = {"production", "development"}}
    server = {
      type = "table",
      schema = {
        fields = {
          host = {type = "url", default = "http://example.com"},
          port = {type = "number", func = server_port, default = 80}
        }
      }
    }
  }
}

这样的配置将允许用户将配置发布到您的插件,如下所示:

curl -X POST http://kong:8001/services/<service-name-or-id>/plugins \
    -d "name=my-custom-plugin" \
    -d "config.environment=development" \
    -d "config.server.host=http://localhost"

以下内容将在handler.lua中提供:

-- handler.lua
local BasePlugin = require "kong.plugins.base_plugin"
local CustomHandler = BasePlugin:extend()

function CustomHandler:new()
  CustomHandler.super.new(self, "my-custom-plugin")
end

function CustomHandler:access(config)
  CustomHandler.super.access(self)

  kong.log.inspect(config.environment) -- "development"
  kong.log.inspect(config.server.host) -- "http://localhost"
  kong.log.inspect(config.server.port) -- 8080
end

return CustomHandler

您还可以在Key-Auth插件源代码中查看schema的真实示例。

微服务 API 网关 Kong 插件开发 – 文件结构

原文地址:https://docs.konghq.com/1.1.x/plugin-development/file-structure/ (不能保证所有的翻译都是准确无误的,所有如有翻译的不准确或错误之处,请一定记得查看原文,并欢迎留言指出)。

本章假定你已经会使用Lua语言

介绍

将您的插件视为一组Lua模块。本章中描述的每个文件都被视为一个单独的模块。如果他们的名字遵循这个约定,Kong将检测并加载你的插件的模块:

kong.plugins.<plugin_name>.<module_name>

您的模块当然需要通过package.path变量访问,可以通过lua_package_path配置属性调整您的需求。但是,安装插件的首选方法是通过LuaRocks,它与Kong本身集成。有关LuaRocks安装的插件的更多信息,请参阅本指南后面的内容。

为了让Kong意识到必须查找插件的模块,你必须将它添加到配置文件中的plugins属性中,这是一个以逗号分隔的列表。例如

plugins = bundled,my-custom-plugin # 你的插件名称

或者,如果您不想加载任何自带的插件:

plugins = my-custom-plugin  # 你的插件名称

现在,Kong将尝试从以下命名空间加载几个Lua模块:

kong.plugins.my-custom-plugin.<module_name>

其中一些模块是必需的(例如handler.lua),有些是可选的,并且允许插件实现一些额外的功能(例如api.lua以扩展Admin API)。现在让我们准确描述您可以实现的模块以及它们的用途。

基本插件模块

在最基本的形式中,插件包含两个必需的模块:

simple-plugin
├── handler.lua
└── schema.lua
  • 每个函数将在请求的生命周期中的所需时刻运行。
  • 由用户。此模块保存该配置的模式并在其上定义规则,以便用户只能输入有效的配置值。

高级插件模块

有些插件可能需要与Kong更深入地集成:在数据库中拥有自己的表,在Admin API中公开端点等等……每个插件都可以通过向插件添加新模块来完成。如果它实现了所有可选模块,那么插件的结构如下:

complete-plugin
├── api.lua
├── daos.lua
├── handler.lua
├── migrations
│   ├── cassandra.lua
│   └── postgres.lua
└── schema.lua

以下是要实施的可能模块的完整列表以及其目的的简要说明。 本指南将详细介绍,让您掌握其中的每一个文件。

模块文件名称 是否必须 描述
api.lua No 定义Admin API中可用的端点列表,以与插件处理的实体自定义实体进行交互。
daos.lua No 定义DAO(数据库访问对象)列表,这些DAO是插件所需并存储在数据存储区中的自定义实体的抽象。
handler.lua Yes 一个接口的实现。每个函数都由Kong在请求的生命周期中的所需时刻运行。
migrations/xxxx.lua No 给定数据存储的相应迁移。只有当您的插件必须在数据库中存储自定义实体并通过daos.lua定义的其中一个DAO与它们进行交互时,才需要进行迁移。
schema.lua Yes 保存插件配置的架构,以便用户只能输入有效的配置值。

Key-Auth 插件是具有此文件结构的插件的示例。 有关详细信息,请参阅其源代码

Openresty 第三方库 Lua_resty_http 使用教程

lua_resty_http是一个第三方 openresty 库,基于 Openresty/ngx_lua 的HTTP客户端,支持POST方法上传数据,刚好项目中用到需要从网关中发起请求,于是就用到这个库,把使用方式在这里分享一下。

安装第三方库lua_resty_http

第一步

首先找到项目地址:https://github.com/pintsized/lua-resty-http

第二步

然后将 lua-resty-http/lib/resty/ 目录下的 http.lua  http_headers.lua 两个文件拷贝到 /usr/local/openresty/lualib/resty 目录下即可,OpenResty 安装目录为 /usr/local/openresty)。不需要重启。(少数需要清空 Openresty shared_dict 数据的情况需要重启 )。

代码示例

local function HttpUtil(jwt_data)
    -- 引入第三方库
    local http = require "resty.http"
    local httpc = http.new()
    -- 需要post的参数
    local str = "aaa=1&bbb=2%ccc=3"
    -- 请求地址
    local url = "http://wwww.xxxxxxx.com"
    local res, err = httpc:request_uri(url, {
        method = "POST",
        body = str,
        headers = {
            ["Content-Type"] = "application/x-www-form-urlencoded",
            ["Content-Length"] = #str,
        }
    })

    -- 返回值
    ngx.say(res.body)
    ngx.say(res.status)
    ngx.say(res.headers)

    return res.body,res.status
end

参数详解

请求参数

参数名 描述
version HTTP版本号,目前支持1.0或1.1
method HTTP方法
path 路径
query 查询字符串,表示为文字字符串或Lua table
headers 请求 header table
body 请求体作为字符串或迭代器函数(请参阅get_client_body_reader)。
ssl_verify 验证SSL证书是否与主机名匹配

返回参数

请求成功后,res将包含以下字段:

参数名 描述
status 状态码
reason 状态原因短语
headers headers 类型是table ,相同名称的header会合并成一个table
has_body bool值,指示是否有要读取的正文
body_reader 迭代器函数,用于以流方式读取正文。
read_body 一种将整个主体读入字符串的方法。
read_trailers 读取正文后,合并标题下trailers的方法。

参考链接