标签归档:Nginx

如何在 Kong 和 OpenResty 中使用环境变量 os.getenv()

在项目中有时会遇到使用系统环境变量的问题,但是直接使用 os.getenv() 是不可行的,不仅是在 Kong 中,在 OpenResty 也都是不可以的,原因是 Kong 是基于 OpenResty ,OpenResty 是基于 Nginx 的,而Nginx在启动的时候,会把环境中所有的环境变量都清除掉,我们可以从Nginx的官方文档中看到这段描述:http://nginx.org/en/docs/ngx_core_module.html#env 

By default, nginx removes all environment variables inherited from its parent process except the TZ variable.

具体可以参考春哥在这个issue下的回复:https://github.com/openresty/lua-nginx-module/issues/601

所以需要在你的nginx.conf文件中把你需要的环境变量声明一下:

events{
  ...
}

env PATH;
env USERNAME;

然后重启一下Nginx,就可以直接在你的 Lua 代码中使用 os.getenv("USERNAME") 获取环境变量。

在 Kong 中使用 os.getenv()

那如果我想在kong的插件中是使用os.getenv()要怎么操作呢?

在 Kong 里面,我们看到Kong生成的 Nginx配置文件是不可修改的,即使你修改了,Kong 还是会生成恢复成修改前,那么这个时候就要用到 Kong 的一个自定义 Nginx 配置文件功能。详情可以看这俩issue:

那么实际操作起来就很简单了,新建一个custom-nginx.conf文件,里面和上文一样,把你需要使用到的环境变量写到这个配置文件中。

events{
  ...
}

env PATH;
env USERNAME;

接着重启 Kong 即可。不过这里要注意的是,重启的时候,需要带上一个参数--nginx-conf。不过不要搞错的是-c参数是kong的配置文件参数,--nginx-conf 是自定义Nginx模板配置参数

 kong restart -c kong.conf --nginx-conf custom-nginx.conf

重启成功之后,就可以在 Kong 的插件中使用代码中使用 os.getenv("USERNAME") 来获取环境变量了。

在 Ubuntu 上安装 Kong

安装包

首先下载配置的相应软件包:

企业试用用户应从其欢迎电子邮件中下载其包,并在步骤1之后将其许可保存到/etc/kong/license.json

APT存储库

您也可以通过APT安装Kong; 按照下面页面上“Set Me Up”部分的说明,将分布设置为适当的值(lsb_release -sc)(例如,precise)和组件到main

安装

  1. 安装Kong 如果要下载程序包,请执行:
      $ sudo apt-get update
      $ sudo apt-get install openssl libpcre3 procps perl
      $ sudo dpkg -i kong-1.3.0.*.deb
    

    如果您正在使用apt存储库执行:

      $ sudo apt-get update
      $ sudo apt-get install -y apt-transport-https curl lsb-core
      $ echo "deb https://kong.bintray.com/kong-deb `lsb_release -sc` main" | sudo tee -a /etc/apt/sources.list
      $ curl -o bintray.key https://bintray.com/user/downloadSubjectPublicKey?username=bintray
      $ sudo apt-key add bintray.key
      $ sudo apt-get update
      $ sudo apt-get install -y kong
    
  2. 准备数据库或声明性配置文件无论是否有数据库,Kong都可以运行。

    使用数据库时,您将使用 kong.conf 配置文件在启动时设置Kong的配置属性,并将数据库用作所有已配置实体的存储,例如Kong代理所在的 Routes 和 Services 。

    不使用数据库时,您将使用kong.conf的配置属性和kong.yml文件来将实体指定为声明性配置。

    使用数据库

    配置Kong以便它可以连接到您的数据库。Kong支持PostgreSQL 9.5+Cassandra 3.x.x作为其数据存储。

    如果您使用Postgres,请在开始Kong之前配置数据库和用户,即:

     CREATE USER kong; CREATE DATABASE kong OWNER kong;
    
    然后执行Kong的数据迁移:
    
      $ kong migrations bootstrap [-c /path/to/kong.conf]
    

    对于Kong 小于0.15的注意事项:如果Kong版本低于0.15(最高0.14),请使用up子命令而不是bootstrap。另请注意,如果Kong 小于0.15,则不应同时进行迁移;只有一个Kong节点应该一次执行迁移。对于0.15,1.0及以上的Kong,此限制被取消。

    不使用数据库

    如果要在无DB模式下运行Kong,则应首先生成声明性配置文件。以下命令将在当前文件夹中生成kong.yml文件。它包含有关如何填写它的说明。

     $ kong config init
    

    填写kong.yml文件后,编辑您的kong.conf文件。将数据库选项设置为off,将declarative_config选项设置为kong.yml文件的路径:

     database = off
      declarative_config = /path/to/kong.yml
    
  3. 启动Kong
     $ kong start [-c /path/to/kong.conf]
    
  4. 使用KongKong正在运行
      $ curl -i http://localhost:8001/

使用 Docker 安装 Kong

有关如何在Docker中使用Kong的详细信息可以在镜像图像的DockerHub存储库中找到:kong。 我们还有一个Docker Compose template,内置编排和可扩展性。

使用数据库

这是一个快速示例,显示如何将Kong容器连接到Cassandra或PostgreSQL容器。

  1. 创建一个Docker network

    您需要创建一个自定义网络,以允许容器相互发现和通信。在此示例中,kong-net是网络名称,您可以使用任何名称。

     $ docker network create kong-net
    
  2. 启动数据库

    如果您想使用Cassandra容器:

      $ docker run -d --name kong-database \
                --network=kong-net \
                -p 9042:9042 \
                cassandra:3
    

    如果您想使用PostgreSQL容器:

      $ docker run -d --name kong-database \
                --network=kong-net \
                -p 5432:5432 \
                -e "POSTGRES_USER=kong" \
                -e "POSTGRES_DB=kong" \
                postgres:9.6
    
  3. 准备数据库

    使用临时Kong容器运行迁移:

      $ docker run --rm \
      --network=kong-net \
      -e "KONG_DATABASE=postgres" \
      -e "KONG_PG_HOST=kong-database" \
      -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
      kong:latest kong migrations bootstrap
    

    在上面的示例中,配置了Cassandra和PostgreSQL,但您应该使用cassandrapostgres更新KONG_DATABASE环境变量。
    对于Kong 小于0.15的注意事项:如果Kong版本低于0.15(最高0.14),请使用up子命令而不是bootstrap。另请注意,如果Kong 版本小于0.15,则不应同时进行迁移;只有一个Kong节点应该一次执行迁移。对于0.15,1.0及以上的Kong,此限制被取消。

  4. 启动Kong

    迁移运行并且数据库准备就绪后,启动一个将连接到数据库容器的Kong容器,就像临时迁移容器一样:

      $ docker run -d --name kong \
      --network=kong-net \
      -e "KONG_DATABASE=postgres" \
      -e "KONG_PG_HOST=kong-database" \
      -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
      -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
      -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
      -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
      -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
      -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
      -p 8000:8000 \
      -p 8443:8443 \
      -p 8001:8001 \
      -p 8444:8444 \
      kong:latest
    
  5. 使用Kong

    Kong正在运行:

      $ curl -i http://localhost:8001/
    

    通过5分钟的快速入门快速学习如何使用Kong。

无数据库模式

在无DB模式下启动Kong所涉及的步骤如下:

  1. 创建一个Docker network

    这与Pg / Cassandra指南中的相同。我们也使用kong-net作为网络名称,它也可以改为其他东西。

      $ docker network create kong-net
    

    在无DB模式下运行Kong并不严格需要此步骤,但如果您希望将来添加其他内容(如Redis群集备份的速率限制插件),这是一个很好的预防措施。

  2. 创建Docker volume

    对于本指南的目的,Docker卷是主机内的一个文件夹,可以将其映射到容器中的文件夹中。卷有一个名称。在这种情况下,我们将命名我们的kong-vol

      $ docker volume create kong-vol
    

    您现在应该能够检查volume:

      $ docker volume inspect kong-vol
    

    结果应该类似于:

      [
          {
              "CreatedAt": "2019-05-28T12:40:09Z",
              "Driver": "local",
              "Labels": {},
              "Mountpoint": "/var/lib/docker/volumes/kong-vol/_data",
              "Name": "kong-vol",
              "Options": {},
              "Scope": "local"
          }
      ]
    

    注意MountPoint条目。我们将在下一步中使用该路径。

  3. 准备声明性配置文件

    声明性配置格式指南中描述了语法和属性。

    添加您需要的任何核心实体(服务,路由,插件,消费者等)。

    在本指南中,我们假设您将其命名为kong.yml。

    将其保存在上一步中提到的MountPoint路径中。

    就本指南而言,这将是/var/lib/docker/volumes/kong-vol/_data/kong.yml

  4. 在无DB模式中启动Kong

    虽然可以仅使用KONG_DATABASE=off来启动Kong容器,但通常还需要通过KONG_DECLARATIVE_CONFIG变量名称将声明性配置文件作为参数包含在内。为此,我们需要从容器中使文件“visible”。我们使用-v标志来实现这一点,它将kong-vol卷映射到容器中的/usr/local/kong/declarative文件夹。

      $ docker run -d --name kong \
      --network=kong-net \
      -v "kong-vol:/usr/local/kong/declarative" \
      -e "KONG_DATABASE=off" \
      -e "KONG_DECLARATIVE_CONFIG=/usr/local/kong/declarative/kong.yml" \
      -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
      -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
      -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
      -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
      -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
      -p 8000:8000 \
      -p 8443:8443 \
      -p 8001:8001 \
      -p 8444:8444 \
      kong:latest
    
  5. 使用Kong

    Kong应该正在运行,它应该包含一些以Kong .yml添加的实体。

      $ curl -i http://localhost:8001/
    

    例如,获取服务列表:

      $ curl -i http://localhost:8001/services

 

Kong 1.3发布!支持原生gRPC代理,上游双向TLS认证,以及更多功能

原文地址:https://konghq.com/blog/kong-1-3-released/

今天,我们很高兴地宣布推出Kong 1.3!我们的工程团队和出色的社区为此版本提供了许多功能和改进。基于1.2版本的成功,Kong 1.3是Kong的第一个版本,它本身支持gRPC代理,上游相互TLS身份验证,以及一系列新功能和性能改进。

请阅读以下内容,了解有关Kong 1.3的新功能,改进和修复的更多信息,以及如何利用这些令人兴奋的变化。 请花几分钟时间阅读我们的更新日志以及升级路径以获取更多详细信息。

原生gRPC代理

我们观察到越来越多的用户转向微服务架构,并听到用户表达他们对本机gRPC代理支持的兴趣。Kong 1.3通过支持gRPC本地代理来解决这个问题,为支持gRPC的基础架构带来更多控制和可见性。

主要优点:

  • 简化运营流程。
  • 为gRPC服务添加A / B测试,自动重试和断路,以提高可靠性和正常运行时间。
  • 更多的可观测性
  • 针对gRPC服务的日志记录,分析或Prometheus集成?Kong让你满意。

主要功能:

  • 新协议:Route和Service实体的protocol属性现在可以设置为grpcgrpcs,这对应于通过明文HTTP/2(h2c)的gRPC和通过TLS HTTP/ 2(h2)的gRPC。

上游双向TLS认证

Kong长期以来一直支持与上游服务的TLS连接。 在1.3中,我们添加了对Kong的支持,以提供特定证书,同时与上游握手以提高安全性。

主要优点:

  • 能够使用证书与上游服务握手使得Kong在需要强大的身份验证保证的行业中更加出色,例如金融和医疗保健服务。
  • 安全性更好
  • 通过提供可信证书,上游服务将确定传入请求是由Kong转发的,而不是恶意客户端。
  • 对开发人员更友好
  • 您可以使用Kong将需要相互TLS身份验证的服务转换为与开发人员无关的方法(例如OAuth)。

主要功能:

  • 新配置属性:Service实体具有新字段client_certificate。如果设置,当Kong尝试与服务握手时将使用相应的证书。

Sessions 插件

在Kong 1.3中,我们开放了Sessions插件(之前仅在Kong Enterprise中提供)供所有用户使用。 结合其他身份验证插件,它允许Kong记住之前已经过身份验证的浏览器用户。 您可以在此处阅读详细的文档

NGINX CVE修复

Kong 1.3附带NGINX HTTP/2模块(CVE-2019-9511CVE-2019-9513CVE-2019-9516)的修复程序。 我们还发布了Kong 1.0.4,1.1.3,1.2.2来修补旧版Kong中的漏洞,以防不能立即升级到1.3。

OpenResty Version Bump

OpenResty的版本已经发布到最新的OpenResty版本 - 1.15.8.1,该版本基于Nginx 1.15.8。 此版本的OpenResty在关闭上游keepalive连接,ARM64架构支持和LuaJIT GC64模式时带来了更好的性能。 最引人注目的变化是,由于LuaJIT编译器生成更多本机代码,OpenResty更有效地存储请求上下文数据,因此使用密钥身份验证在基线代理基准测试中,Kong现在运行速度提高约10%。 

Kong 1.3的其他新功能

按任何请求 header 路由

  • Kong的路由器现在能够通过任何请求头(不仅是Host)匹配路由。
  • 这允许对服务之间路由传入流量的方式进行精细控制。
  • 参阅此处的文档

最少连接负载平衡

  • Kong现在可以将流量发送到连接数最少的上游服务。
  • 在某些用例中改善上游负载分配。
  • 参阅此处的文档

数据库导出

  • 新添加的kong config db_export CLI命令可用于创建数据库内容转储到YAML文件中,该文件适用于声明性配置或稍后导回数据库。
  • 这样可以更轻松地创建声明性配置文件。
  • 这使得Kong配置的备份和版本控制变得更加容易
  • 参阅此处的文档

主动关闭上游keepalive连接

  • 在旧版本的Kong中,上游连接永远不会被Kong关闭。这可能导致竞争条件,因为Kong可能会尝试重新使用keepalived连接,而上游尝试关闭它。
  • 如果您在Kong error.log中看到“upstream prematurely closed connection”错误,则此版本应显着减少甚至消除部署中的此错误。
  • 添加了新的配置指令来控制此行为,请阅读完整的更新日志以了解更多信息。

更多的监听标志支持

  • 特别是reuseport标志,如果Kong worker数量很大,可用于改善负载分配和延迟抖动。
  • 还添加了deferredbind标志支持。您可以查看NGINX listen指令文档以了解使用它们的效果。

其他改进和错误修复

Kong 1.3还包含有关存储CA证书(没有私钥的证书),Admin API接口和更多PDK功能的新实体的改进。 我们还修复了很多错误。由于此版本中有大量新功能,因此我们无法在此博客文章中介绍所有这些内容,而是鼓励您在此处阅读完整的更新日志

我们还在kong.conf模板中添加了一个新的部分,以更好地解释注入NGINX指令的功能。 对于具有仅添加几个NGINX指令的自定义模板的用户,我们建议切换使用注入的NGINX指令,以获得更好的可升级性。

与往常一样,Kong 1.3的文档可在此处获得。 此外,如上所述,我们将在后续帖子和社区电话中讨论1.3中的主要功能,敬请期待!

感谢我们的用户,贡献者和核心维护者社区,感谢您对Kong的开源平台的持续支持。 请试试Kong 1.3,一定要告诉我们您的想法

Kong社区

像往常一样,随时可以就我们的社区论坛Kong Nation提出任何问题。 从您的反馈中学习将使我们能够更好地理解任务关键用例并不断改进Kong。

基于OpenResty 的 WEB 框架 Lor 安装初探

  • 项目介绍:
    • Lor是一个运行在OpenResty上的基于Lua编写的Web框架.
    • 路由采用Sinatra风格,Sinatra是Ruby小而精的web框架.
    • API基本采用了Express的思路和设计,Node.js跨界开发者可以很快上手.
    • 支持插件(middleware),路由可分组,路由匹配支持string/正则模式.
    • lor以后会保持核心足够精简,扩展功能依赖middleware来实现. lor本身也是基于middleware构建的.
    • 推荐使用lor作为HTTP API Server,lor也已支持session/cookie/html template等功能.
    • 框架简单示例项目lor-example
    • 框架全站示例项目openresty-china
  • 项目地址: https://github.com/sumory/lor
  • 文档地址: http://lor.sumory.com

安装lor框架

git clone https://github.com/sumory/lor
cd lor 
make install

此时可能会有报错如下:

/usr/bin/env: "resty": 没有那个文件或目录

原因为lor没有找到resty的执行目录,这个时候只需要找到resty的执行目录并软链过去即可:

sudo ln -s /usr/local/openresty/bin/resty /usr/bin/resty

框架自带一个示例项目,运行一下代码

lord new lor_demo

启动

然后就能在框架中看到lor_demo文件夹,执行如下命令

cd lor_demo
lord start

打开浏览器,输入http://localhost:8888/正常访问,即正常安装。

使用 konga 来管理微服务 API 网关 kong

微服务网关kong有比较多个后台管理面板,比如比较简单的kong-dashboard,还有konga,之前在初探kong的时候,使用的就是比较简单的kong-dashboard,很多功能都没有,而且最近由于kong官方更新比较频繁,1.0之后的kong-dashboard就已经不兼容了,频繁报错,所有今天我就来使用一下另一款kong的后台管理面板:konga

安装konga

在开始安装之前,需要准备的有:

  • 一个已经安装好的kong环境
  • Nodejs >= 8 (推荐8.11.3 LTS)
  • npm

关于这三者的安装,我这里就不赘述了,kong的安装可以在博客的相关文章查看。在做好准备工作之后,就开始安装:

安装npm依赖

$ git clone https://github.com/pantsel/konga.git
$ cd konga
$ npm i

在执行npm i之后,由于会引入很多的npm包,所以可能会有报错,比如我这里就遇到了一些权限问题,比如:

Unable to save binary /home/qianyugang/soft/konga/node_modules/node-sass/vendor/linux-x64-64 : { Error: EACCES: permission denied, mkdir '/home/thinkpad/soft/konga/node_modules/node-sass/vendor'

如果出现了如上错误,可以把最后一个命令修改为

sudo npm i --unsafe-perm=true --allow-root

执行之后,可能会提示:

bower bootstrap-switch extra-resolution Unnecessary resolution: bootstrap-switch#~3.3.4
added 69 packages from 77 contributors and audited 3120 packages in 4.654s
found 275 vulnerabilities (125 low, 24 moderate, 126 high)
  run `npm audit fix` to fix them, or `npm audit` for details

意思是有一些包没有执行好,那么我们就按照它的提示执行一下:

sudo npm audit fix --unsafe-perm=true --allow-root

基本就可以解决npm依赖包的问题了。总之这里是有可能出现各种各样的npm问题,依次解决即可。

初始化数据库

npm的依赖都安装完成之后,就需要复制一下konga目录下的.env_example文件

cp .env_example env

然后把其中的一些配置项目都填写上去。具体的配置项可以查看 https://github.com/pantsel/konga#environment-variables 。这里就主要把以下三项配置好:

DB_ADAPTER=postgres
PORT=1337
DB_URI=postgresql://kong_user:kong_pass@localhost:5432/konga

注意这个DB_URI一定要填写完整,这里的数据库我用的是postgres,konga其实还支持mysql,mongo等多种数据库,这里就不赘述了。配置完成之后,需要登上你自己的postgres数据库,然后执行如下命令新建数据库:

CREATE DATABASE "konga" WITH ENCODING='UTF8';

新建了数据库之后,,执行如下命令,来初始化数据库:

node ./bin/konga.js prepare --adapter postgres --uri postgresql://kong_user:kong_pass@localhost:5432/konga

出现如下提示之后,数据库这一块就完成:

Preparing database...
debug: Hook:api_health_checks:process() called
debug: Hook:health_checks:process() called
debug: Hook:start-scheduled-snapshots:process() called
debug: Hook:upstream_health_checks:process() called
debug: Hook:user_events_hook:process() called
debug: Seeding User...
debug: User seed planted
debug: Seeding Kongnode...
debug: Kongnode seed planted
debug: Seeding Emailtransport...
debug: Emailtransport seed planted
debug: Database migrations completed!

启动konga

执行如下命令:

npm start

看到如下小帆船的图,成功启动


   Sails              <|    .-..-.
   v0.12.14            |\
                      /|.\
                     / || \
                   ,'  |'  \
                .-'.-==|/_--'
                `--'-------' 
   __---___--___---___--___---___--___
 ____---___--___---___--___---___--___-__

最后,打开浏览器输入http://localhost:1337/,就可以进入konga的管理界面了。会出现一个让你注册的界面,注册登录一下,然后配置一下kong的admin-api链接,大功告成,打完收工。

微服务 API 网关 Kong 中文文档发布

由于项目的原因,最近的几个月一直在学习微服务的API网关 Kong ,在这里做一个简单介绍,是一个云原生,高效,可扩展的分布式 API 网关。 自 2015 年在 github 开源后,广泛受到关注,目前已收获 1.68w+ 的 star,其核心价值在于高性能和可扩展性。由于对项目的积极维护,Kong被广泛用于从初创公司到全球5000强以及政府机构的生产中。从技术角度来说,Kong是基于Openresty的一个莹莹,Openresty是基于Nginx的,使用的语言是Lua。

所以在学习过程中,首要是需要看官方文档,由于项目比较新,所以暂时没有中文文档,我就想着,反正文档总是要全部看一遍的,不如自己翻译一份好了,于是乎就有了这个项目:Kong的文档中文版。欢迎大家star&fork。

由于自己不是英语专业,而且主要目的是学习Kong,所以采用的是人工+机翻结合的方式,如果有遇到翻译的不够通顺,或者对于翻译的语句有歧义的地方,麻烦一定点击官网英文文档https://docs.konghq.com/ 查看,并且欢迎提 PR 提修改意见。另,由于kong的文档本身也在不断增加和完善当中,如果有遇到没有即使更新翻译的状况欢迎提issue,我会不断补充的。

todo:

  • 目前文档中的超链接都是链接的英文原文,后续会慢慢改成中文内链。
  • 会在每一页文档里面附上单独的英文原文链接,以便做对照。
  • 会添加kong自带的插件文档。

本文档是基于 https://docs.konghq.com/1.1.x/ 1.1.x 版本,目前官网已经更新至 1.2.x 版本,如果使用的最新版本,请查看 https://docs.konghq.com 并注意差别。

Lua OpenResty 使用 protobuf 和 rabbitmq AMQP 发送和接收消息

项目中有个需求,需要使用Google的protobuf作为压缩协议,然后使用rabbitmq AMQP来发送和接收消息,在研究使用这两个工具中,遇到了有一些坑,之前有写了两篇来介绍分别使用,《 在 lua 中使用 protobuf》和《Lua OpenResty 使用rabbitmq AMQP协议发送和接收消息》 ,这里我们来结合使用一下,然后顺便解决一下lua的相关库的使用问题。

protobuf 相关步骤

1.安装 protoc

首先安装依赖库

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

2.生成pb文件

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

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

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

3.安装lua的protobuf库

这里我们使用的是lua-protobuf库。 lua-protobuf实际上是一个纯C的protobuf协议实现,和对应的Lua绑定。

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

可以使用 luarocks 安装lua-protobuf

luarocks install lua-protobuf

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

rabbitmq AMQP 相关步骤

1.安装第三方库

如果想要在openresty中使用AMQP协议发送和接收消息的话,需要使用到一个第三方库。地址为:https://github.com/4mig4/lua-amqp 。这里你会发现,这个库和《Lua OpenResty 使用rabbitmq AMQP协议发送和接收消息》文章中介绍使用的库不一样,确实是的,因为在使用过程中我发现,之前的那个库还有一些功能不完善,这里使用的这个库是作者fork了之前的那个项目,并改进了里面许多功能。所以最终我选用了这个库来开发。

安装方式为,使用luarocks安装,由于这个库没有push到https://luarocks.org/ 仓库当中,所以我们需要使用到其中的声明文件安装。把项目clone下来之后,找到这个文件 amqp-1.0-4.rockspec 这个文件,然后执行:

luarocks build amqp-1.0-4.rockspec

这里直接执行,可能会报错,报错信息为unrecognized filename extension,意思是识别不了文件格式之类的,这里需要把这个文件中的

source = {
   url = "https://github.com/4mig4/lua-amqp.git",
   tag = "",
}

修改为:

source = {
   url = "https://github.com/4mig4/lua-amqp",
   tag = "",
}

然后继续执行,执行完成之后,就可以在/usr/local/share/lua/5.1目录中看到这个第三方库了,如果整理的文件夹名字不是amqp,而是一个amqp加版本号,可以手动直接把文件夹修改为amqp即可。到此第三方库安装完成。

在使用过程中,我发现一个问题,作者在fork了项目之后,增加了CQUEUES, NGX.SOCKET, SOCKET一些通讯协议功能,但是我本地环境直接运行会报错,查看了一些源码,发现是我的本地环境的cqueues支持有点问题,而作者把这个作为最优先的协议,那么我就把源码文件https://github.com/4mig4/lua-amqp/blob/master/amqp/init.lua 中的local use_cqueues = true改为local use_cqueues = false,即关闭cqueues来使用了。

发送和接收消息

假设你的proto结构体是这样的:

syntax = "proto3";
package aa.bb;
message EventEnvelope {
  string id = 1; 
  int64 created_ts = 2; 
  string server_hash = 3;
  int64 happened_ts = 4; 
  oneof body {
    aa.bb.LoggedOut logged_out = 1501;
  }
message LoggedOut {
  int64 union_id = 1;
  string app_key = 2;
}

发送消息

local function send_pb()

    -- 引入pb库
    local pb   = require "pb"
    -- 加载pb文件
    assert(pb.loadfile("xxxxx/common.pb"))

    -- 这里是你需要发送的pb消息,注意这里是支持嵌套的,如果你的protobuf文件里面有one of,可以直接使用多层嵌套
    -- 这里就根据你自己protobuf结构来就好
    local data    = {
        created_ts  = ngx.now() * 1000,
        server_hash = 'localhost',
        happened_ts = ngx.now() * 1000,
        id          = 8376548368364,
        logged_out  = {
            union_id = 123455666,
            app_key  = xxxxxxxx
        }
    }

    -- 生成需要发送的消息,这个消息是proto压缩之后的,这个`EventEnvelope` 就是最外层的结构体
    local messages = assert(pb.encode("aa.bb.EventEnvelope", data))

    -- 引入amqp第三方库
    local amqp        = require "amqp"

    -- 里面的一些参数就不再赘述了,都是rabbitmq的一些参数
    local ctx = amqp.new({
        role        = "publisher",
        exchange    = "exchangexxxx",
        ssl         = false,
        user        = "guest",
        password    = "guest",
        auto_delete = false,
        routing_key = "routing_keyxxxxx",
        passive     = true,
        no_ack      = true,
        no_wait     = false,
    })
    ctx:connect("127.0.0.1", port)
    ctx:setup()
    local ok, err = ctx:publish(messages)
    if not ok then
        ngx.log(ngx.ERR, "[ -- rabbitmq send failed : -- ] " .. err)
    else
        ngx.log(ngx.ERR, "[ -- rabbitmq send success ]")
    end
end

send_pb("this is a message")

接收消息

可以新建一个consume_queue.lua文件,然后如下代码:


-- 依旧是引入
local amqp            = require "amqp"
local pb              = require "pb"

-- 这里是回调函数
local function consume_local(f)
    print(f) -- 这里就是消息的所有信息,里面包含了properties ,body,frame等信息 
    print(f.body) -- 这里就是消息的主体

       -- 加载pb文件
    assert(pb.loadfile("xxxxx/common.pb"))
    -- 解析消息
    local data = assert(pb.decode("aa.bb.EventEnvelope", f.body))
    print(data) 

end

local ctx = amqp:new({
    role        = "consumer",
    queue       = "eventbus1", -- 这里可以自定义
    exchange    = "exchangexxxx",
    ssl         = false,
    user        = "guest",
    password    = "guest",
    no_wait     = false,
    routing_key = "routing_keyxxxxx",
    auto_delete = false, -- 是否自动删除消息
    no_ack      = true,
    exclusive   = false, -- 是否为排他队列
    callback    = consume_local, -- 回调函数
    durable     = true,
    passive     = false,
    type        = "topic"

})

ctx:connect("127.0.0.1", port)

local ok, err = ctx:consume()

执行命令开始消费消息

/usr/local/openresty/bin/resty consume_queue.lua

微服务 API 网关 Kong 代理中文文档

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

代理

简介

在本文档中,我们将通过详细解释其路由功能和内部工作原理,来了解Kong的代理功能。

Kong公开了几个可以通过两个配置属性调整的接口:

  • proxy_listen:它定义了一个地址/端口列表,Kong将接受来自客户端的公共流量并将其代理到您的上游服务(默认为8000)。
  • admin_listen:它还定义了一个地址和端口列表,但是这些应该被限制为仅由管理员访问,因为它们暴露了Kong的配置功能:Admin API(默认为8001)。

注意:从1.0.0开始,API实体已被删除。本文档将介绍使用新路由和服务实体进行代理。
如果您使用的是0.12或更低版本,请参阅本文档的旧版本。

相关术语

  • client:指下游 downstream 客户向Kong的代理端口发出请求。
  • upstream service:指位于Kong后面的您自己的 API/service ,转发客户端请求。
  • Service:顾名思义,服务实体是每个上游服务的抽象。比如说服务可以是数据转换微服务,一个计费API等。
  • Route:这是指Kong Routes实体。路由是进入Kong的入口点,并定义要匹配的请求的规则,并路由到给定的服务。
  • Plugin:这指的是Kong“插件”,它们是在代理生命周期中运行的业务逻辑。可以通过Admin API配置插件 – 全局(所有传入流量)或特定路由和服务。

概览

从高层次的角度来看,Kong在其配置的代理端口上监听HTTP流量(默认情况下为80008443)。Kong将根据您配置的路由评估任何传入的HTTP请求,并尝试查找匹配的路由。如果给定的请求与特定Route的规则匹配,Kong将处理代理请求。由于每个Route都链接到一个Service,因此Kong将运行您在Route及其相关服务上配置的插件,然后在上游代理请求。

您可以通过Kong的Admin API管理Routes。Routes 的机 hostspsths methods 属性定义用于匹配传入HTTP请求的规则。

如果Kong收到的请求无法与任何已配置的路由匹配(或者如果没有配置路由),它将响应:

HTTP/1.1 404 Not Found
Content-Type: application/json
Server: kong/<x.x.x>

{
    "message": "no route and no Service found with those values"
}

提醒:如何配置服务

配置服务快速入门指南介绍了如何通过[Admin API]配置Kong。

通过向Admin API发送HTTP请求来向Kong添加服务:

curl -i -X POST http://localhost:8001/services/ \
    -d 'name=foo-service' \
    -d 'url=http://foo-service.com'
HTTP/1.1 201 Created
...

{
    "connect_timeout": 60000,
    "created_at": 1515537771,
    "host": "foo-service.com",
    "id": "d54da06c-d69f-4910-8896-915c63c270cd",
    "name": "foo-service",
    "path": "/",
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1515537771,
    "write_timeout": 60000
}

该请求指示Kong注册一个名为“foo-service”的服务,该服务指向http://foo-service.com(上游)。

注意:url参数是一个简化参数,用于一次性添加protocolhostportpath

现在,为了通过Kong向这个服务发送流量,我们需要指定一个Route,它作为Kong的入口点:

curl -i -X POST http://localhost:8001/routes/ \
    -d 'hosts[]=example.com' \
    -d 'paths[]=/foo' \
    -d 'service.id=d54da06c-d69f-4910-8896-915c63c270cd'
HTTP/1.1 201 Created
...

{
    "created_at": 1515539858,
    "hosts": [
        "example.com"
    ],
    "id": "ee794195-6783-4056-a5cc-a7e0fde88c81",
    "methods": null,
    "paths": [
        "/foo"
    ],
    "preserve_host": false,
    "priority": 0,
    "protocols": [
        "http",
        "https"
    ],
    "service": {
        "id": "d54da06c-d69f-4910-8896-915c63c270cd"
    },
    "strip_path": true,
    "updated_at": 1515539858
}

我们现在已经配置了一个Route来匹配与给定hostpath匹配的传入请求,并将它们转发到我们配置的foo-service,从而将此流量代理到http://foo-service.com

路由和匹配功能

现在让我们讨论Kong如何匹配针对路由的已配置hostpathmethods属性(或字段)的请求。请注意,所有这三个字段都是可选的,但必须至少指定其中一个。

对于匹配路线的请求:

  • 请求必须包含所有已配置的字段
  • 请求中的字段值必须至少与其中一个配置值匹配(当字段配置接受一个或多个值时,请求只需要其中一个值被视为匹配)

我们来看几个例子。考虑如下配置的路由:

{
    "hosts": ["example.com", "foo-service.com"],
    "paths": ["/foo", "/bar"],
    "methods": ["GET"]
}

与此Route匹配的一些可能请求如下所示:

GET /foo HTTP/1.1
Host: example.com
GET /bar HTTP/1.1
Host: foo-service.com
GET /foo/hello/world HTTP/1.1
Host: example.com

所有这三个请求都满足路径定义中设置的所有条件。但是,以下请求与配置的条件不匹配:

GET / HTTP/1.1
Host: example.com
POST /foo HTTP/1.1
Host: example.com
GET /foo HTTP/1.1
Host: foo.com

所有这三个请求仅满足两个配置条件。第一个请求的路径不匹配配置的路径,第二个请求的HTTP方法和第三个请求的host头也均不匹配。

现在我们了解了hosts, paths, 和 methods属性如何协同工作,让我们分别来看每个属性。

host 请求头

基于其host header 来路由请求是通过Kong代理流量的最直接方式,特别是因为这是HTTP host header 的预期用途。Kong可以通过Route实体的hosts字段轻松完成。

hosts接受多个值,在通过Admin API指定它们时必须以逗号分隔:

hosts接受多个值,这些值很容易在JSON有效负载中表示:

curl -i -X POST http://localhost:8001/routes/ \
    -H 'Content-Type: application/json' \
    -d '{"hosts":["example.com", "foo-service.com"]}'
HTTP/1.1 201 Created
...

但由于Admin API还支持form-urlencoded内容类型,因此您可以通过[]表示法指定数组:

curl -i -X POST http://localhost:8001/routes/ \
    -d 'hosts[]=example.com' \
    -d 'hosts[]=foo-service.com'
HTTP/1.1 201 Created
...

要满足此Route的hosts条件,来自客户端的任何传入请求现在必须将其Host header 设置为以下之一:

Host: example.com

或者

Host: foo-service.com

使用通配符主机名

为了提供灵活性,Kong允许您在hosts字段中指定带通配符的主机名。通配符主机名允许任何匹配的host满足条件,从而匹配给定的Route。

通配符主机名必须在域的最左侧或最右侧标签中仅包含一个星号。例子:

  • *.example.com将匹配诸如a.example.com  x.y.example.com
  • example.* 将匹配诸如example.com  example.org

一个完整的例子如下所示:

{
    "hosts": ["*.example.com", "service.com"]
}

将允许以下请求匹配此路由:

GET / HTTP/1.1
Host: an.example.com
GET / HTTP/1.1
Host: service.com

preserve_host属性

代理时,Kong的默认行为是将上游请求的主机头设置为服务主机中指定的hostpreserve_host字段接受一个布尔标志,指示Kong不要这样做。

例如,当preserve_host属性未更改且Route配置如下:

{
    "hosts": ["service.com"],
    "service": {
        "id": "..."
    }
}

client对Kong的可能请求可能是:

GET / HTTP/1.1
Host: service.com

Kong将从Service的主机属性中提取Host头值,并将发送以下上游请求:

GET / HTTP/1.1
Host: <my-service-host.com>

但是,通过使用preserve_host=true配置Route:

{
    "hosts": ["service.com"],
    "preserve_host": true,
    "service": {
        "id": "..."
    }
}

并假设来自客户的相同请求:

GET / HTTP/1.1
Host: service.com

Kong将根据客户端请求保留Host,并将发送以下上游请求:

GET / HTTP/1.1
Host: service.com

请求路径

路由匹配的另一种方式是通过请求路径。 要满足此路由条件,客户端请求的路径必须以paths属性的值之一为前缀。 例如,使用如下配置的Route:

{
    "paths": ["/service", "/hello/world"]
}

以下请求将被匹配:

GET /service HTTP/1.1
Host: example.com
GET /service/resource?param=value HTTP/1.1
Host: example.com
GET /hello/world/resource HTTP/1.1
Host: anything.com

对于这些请求中的每一个,Kong检测到其URL路径以路由的paths之一为前缀。默认情况下,Kong会在不更改URL路径的情况下代理上游请求。

使用路径前缀进行代理时,首先评估最长路径。这允许您定义两个具有两个路径的Routes:/service/service/resource,并确保前者不会“遮蔽”后者。

在路径中使用正则表达式

Kong通过PCRE(Perl兼容正则表达式)支持Route的路径字段的正则表达式模式匹配。您可以同时将路径作为前缀和正则表达式分配给Route。例如,如果我们考虑以下Route:

{
    "paths": ["/users/\d+/profile", "/following"]
}

此Route将匹配以下请求:

GET /following HTTP/1.1
Host: ...
GET /users/123/profile HTTP/1.1
Host: ...

使用PCRE标志(PCRE_ANCHORED)评估提供的正则表达式,这意味着它们将被约束为在路径中的第一个匹配点(root/character)匹配。

评估顺序

如前所述,Kong按长度评估前缀路径:首先评估最长前缀路径。 但是,Kong将根据路由的regex_priority属性从最高优先级到最低优先级来评估正则表达式路径。这意味着考虑以下Routes:

[
    {
        "paths": ["/status/\d+"],
        "regex_priority": 0
    },
    {
        "paths": ["/version/\d+/status/\d+"],
        "regex_priority": 6
    },
    {
        "paths": ["/version"],
    },
    {
        "paths": ["/version/any/"],
    }
]

在这种情况下,Kong将按以下顺序评估针对以下定义的URI的传入请求:

  1. /version/any/
  2. /version
  3. /version/\d+/status/\d+
  4. /status/\d+

始终在正则表达式路径之前评估前缀路径。

像往常一样,请求仍然必须匹配Route的机hostsmethods属性,并且Kong将遍历您的Routes,直到找到匹配最多规则的路由(请参阅[路由优先级] [代理路由优先级])。

捕获组

也支持正则的捕获组,匹配的组将从路径中提取并可用于插件使用。 如果我们考虑以下正则表达式:

/version/(?<version>\d+)/users/(?<user>\S+)

以及以下请求路径:

/version/1/users/john

Kong会将请求路径视为匹配,如果匹配整个Route(考虑hostsmethods字段),则可以从ngx.ctx变量中的插件获取提取的捕获组:

local router_matches = ngx.ctx.router_matches

-- router_matches.uri_captures is:
-- { "1", "john", version = "1", user = "john" }
规避特殊字符

接下来,值得注意的是,根据RFC 3986,在正则表达式中找到的字符通常是保留字符,因此应该是百分比编码(URL编码)。通过Admin API配置具有正则表达式路径的路由时,请务必在必要时对您的有效负载进行URL编码。例如,使用curl并使用application/x-www-form-urlencodeMIME类型:

curl -i -X POST http://localhost:8001/routes \
    --data-urlencode 'uris[]=/status/\d+'
HTTP/1.1 201 Created
...

请注意,curl不会自动对您的有效负载进行URL编码,并注意使用--data-urlencode,它可以防止Kong的Admin API对+字符进行URL解码,并将其解码为一个空的“。

strip_path属性

可能需要指定路径前缀以匹配路由,但不将其包括在上游请求中。为此,请通过配置如下所示的Route来使用strip_path布尔属性:

{
    "paths": ["/service"],
    "strip_path": true,
    "service": {
        "id": "..."
    }
}

启用此标志会指示Kong在匹配此路由并继续代理服务时,不应在上游请求的URL中包含URL路径的匹配部分。例如,以下客户端对上述路由的请求:

GET /service/path/to/resource HTTP/1.1
Host: ...

将导致Kong发送以下上游请求:

GET /path/to/resource HTTP/1.1
Host: ...

同样,如果在启用了strip_path的Route上定义了正则表达式路径,则将剥离整个请求URL匹配序列。 例:

{
    "paths": ["/version/\d+/service"],
    "strip_path": true,
    "service": {
        "id": "..."
    }
}

以下HTTP请求与提供的正则表达式路径匹配:

GET /version/1/service/path/to/resource HTTP/1.1
Host: ...

请求HTTP方法

methods字段允许根据HTTP方法匹配请求。它接受多个值。其默认值为空(HTTP方法不用于路由)。 以下路由允许通过GETHEAD进行路由:

{
    "methods": ["GET", "HEAD"],
    "service": {
        "id": "..."
    }
}

这样的Route将符合以下要求:

GET / HTTP/1.1
Host: ...
HEAD /resource HTTP/1.1
Host: ...

但它与POSTDELETE请求不匹配。在路由上配置插件时,这允许更多粒度。例如,可以想象两个指向同一服务的路由:一个具有无限制的未经身份验证的GET请求,另一个仅允许经过身份验证和速率限制的POST请求(通过将身份验证和速率限制插件应用于此类请求)。

匹配优先事项

Route可以基于其hosts, paths, 和 methods字段定义匹配规则。要使Kong匹配到路由的传入请求,必须满足所有现有字段。 但是,通过允许两个或多个路由配置包含相同值的字段,Kong允许相当大的灵活性 – 当发生这种情况时,Kong应用优先级规则。

规则是:在评估请求时,Kong将首先尝试匹配具有最多规则的路由。

例如,如果两个路由配置如下:

{
    "hosts": ["example.com"],
    "service": {
        "id": "..."
    }
},
{
    "hosts": ["example.com"],
    "methods": ["POST"],
    "service": {
        "id": "..."
    }
}

第二个Route有一个hosts字段和一个methods字段,因此它将首先由Kong评估。通过这样做,我们避免了第一个用于第二个路径的“阴影”调用。

因此,此请求将匹配第一个Route

GET / HTTP/1.1
Host: example.com

这个请求将匹配第二个:

POST / HTTP/1.1
Host: example.com

遵循这个逻辑,如果要使用hosts字段,methods字段和uris字段配置第三个Route,它将首先由Kong评估。

代理行为

上面的代理规则详细说明了Kong如何将传入请求转发到您的上游服务。下面,我们详细说明Kong与HTTP请求与注册路由匹配的时间与请求的实际转发之间内部发生的情况。

1.负载均衡

Kong实现负载平衡功能,以跨上游服务实例池分发代理请求。

您可以通过查看负载平衡来查找有关配置负载平衡的更多信息

2.插件执行

Kong可通过“插件”进行扩展,这些“插件”将自己挂载在代理请求的请求/响应生命周期中。插件可以在您的环境中执行各种操作 和/或 在代理请求上进行转换。

可以将插件配置为全局(针对所有代理流量)或特定 Routes 和 Services运行。 在这两种情况下,您都必须通过Admin API创建插件配置

一旦路由匹配(及其关联的服务实体),Kong将运行与这些实体中的任何一个相关联的插件。在路由上配置的插件在服务上配置的插件之前运行,否则,通常的插件关联规则适用。

这些配置的插件将运行其access阶段,您可以在插件开发指南中找到更多相关信息。

3.代理和上游超时

一旦Kong执行了所有必要的逻辑(包括插件),它就可以将请求转发给您的上游服务。这是通过Nginx的ngx_http_proxy_module完成的。 您可以通过以下服务属性为Kong和给定上游之间的连接配置所需的超时:

  • upstream_connect_timeout:以毫秒为单位定义建立与上游服务的连接的超时。默认为60000
  • upstream_send_timeout:以毫秒为单位定义用于向上游服务发送请求的两个连续写入操作之间的超时。默认为60000
  • upstream_read_timeout:以毫秒为单位定义用于接收来自上游服务的请求的两个连续读取操作之间的超时。默认为60000

Kong将通过 HTTP/1.1 发送请求,并设置以下headers:

  • Host: <your_upstream_host>,如前文所述。
  • Connection: keep-alive,允许重用上游连接。
  • X-Real-IP: <remote_addr>,其中$remote_addr是ngx_http_core_module提供的名称相同的变量。请注意,$remote_addr可能被ngx_http_realip_module覆盖。
  • X-Forwarded-For: <address>,其中<address>是由附加到具有相同名称的请求标头的ngx_http_realip_module提供的$realip_remote_addr的内容。
  • X-Forwarded-Proto: <protocol>,其中<protocol>是客户端使用的协议。在$realip_remote_addr是可信地址之一的情况下,如果提供,则转发具有相同名称的请求头。否则,将使用ngx_http_core_module提供的$scheme变量的值。
  • X-Forwarded-Host: <host>,其中<host>是客户端发送的主机名。在$realip_remote_addr是可信地址之一的情况下,如果提供,则转发具有相同名称的请求头。否则,将使用ngx_http_core_module提供的$host变量的值。
  • X-Forwarded-Port: <port>,其中<port>是接受请求的服务器的端口。在$realip_remote_addr是可信地址之一的情况下,如果提供,则转发具有相同名称的请求头。否则,将使用ngx_http_core_module提供的$server_port变量的值。

所有其他请求headers都由Kong转发。

使用WebSocket协议时会出现一个例外。如果是这样,Kong将设置以下标头以允许升级客户端和上游服务之间的协议:

  • Connection: Upgrade
  • Upgrade: websocket

有关此主题的更多信息,请参见[Proxy WebSocket流量] [proxy-websocket]部分。

4.错误和重试

每当代理期间发生错误时,Kong将使用底层的Nginx重试机制将请求传递给下一个上游。

这里有两个可配置元素:

  1. 重试次数:可以使用retries属性为每个服务配置。有关详细信息,请参阅Admin API
  2. 究竟是什么构成错误:这里Kong使用Nginx默认值,这意味着在与服务器建立连接,向其传递请求或读取响应头时发生错误或超时。

第二个选项基于Nginx的proxy_next_upstream指令。此选项不能通过Kong直接配置,但可以使用自定义Nginx配置添加。有关详细信息,请参阅配置参考。

5.响应

Kong接收来自上游服务的响应,并以流方式将其发送回下游客户端。此时,Kong将执行添加到 Route 和/或 Service 的后续插件,这些插件在header_filter阶段实现一个钩子。

一旦执行了所有已注册插件的header_filter阶段,Kong将添加以下headers,并将完整的headers发送到客户端:

  • Via: kong/x.x.x,其中x.x.x是正在使用的Kong版本。
  • X-Kong-Proxy-Latency: <latency>,其中latency是Kong收到客户端请求和向上游服务发送请求之间的时间(以毫秒为单位)。
  • X-Kong-Upstream-Latency: <latency>,其中latency是Kong等待上游服务响应的第一个字节的时间(以毫秒为单位)。

将标题发送到客户端后,Kong将开始为实现body_filter钩子的 Route和/或Service 执行已注册的插件。由于Nginx的流媒体特性,可以多次调用此钩子。由这样的body_filter挂钩成功处理的上游响应的每个块被发送回客户端。您可以在插件开发指南中找到有关body_filter钩子的更多信息。

配置一个备用路由

作为Kong的代理功能提供的灵活性的实际用例和示例,让我们尝试实现“后备路线”,因此,为了避免Kong响应HTTP 404,“找不到路由”,我们可以捕获这些请求并将它们代理到特殊的上游服务,或者向它应用插件(例如,这样的插件可以使用不同的状态代码或响应终止请求,而不代理请求)。

以下是此类后备路由的示例:

{
    "paths": ["/"],
    "service": {
        "id": "..."
    }
}

正如您所猜测的,任何向Kong发出的HTTP请求实际上都会匹配此Route,因为所有URI都以根字符/为前缀。正如我们从[请求路径] [代理请求路径]部分所知,最长的URL路径首先由Kong评估,因此/路径最终将由Kong最后评估,并有效地提供“后备”路由,仅作为最后的手段。

为路由配置SSL

Kong提供了一种基于每个连接动态提供SSL证书的方法。SSL证书由核心直接处理,并可通过Admin API进行配置。通过TLS连接到Kong的客户端必须支持服务器名称指示扩展才能使用此功能。

SSL证书由Kong Admin API中的两个资源处理:

  • /certificates,存储您的密钥和证书。
  • /snis,将注册证书与Server Name 指示相关联。

您可以在Admin API参考中找到这两种资源的文档。

以下是在给定路由上配置SSL证书的方法:首先,通过Admin API上传您的SSL证书和密钥:

curl -i -X POST http://localhost:8001/certificates \
    -F "cert=@/path/to/cert.pem" \
    -F "key=@/path/to/cert.key" \
    -F "snis=ssl-example.com,other-ssl-example.com"
HTTP/1.1 201 Created
...

snis表单参数是糖参数,直接插入SNI并将上传的证书与其关联。

您现在必须在Kong内注册以下Route。 为方便起见,我们仅使用Hos header 匹配对此Route的请求:

curl -i -X POST http://localhost:8001/routes \
    -d 'hosts=ssl-example.com,other-ssl-example.com' \
    -d 'service.id=d54da06c-d69f-4910-8896-915c63c270cd'
HTTP/1.1 201 Created
...

您现在可以期望Kong通过HTTPS提供路由:

curl -i https://localhost:8443/ \
  -H "Host: ssl-example.com"
HTTP/1.1 200 OK
...

建立连接并协商SSL握手时,如果您的客户端发送ssl-example.com作为SNI扩展的一部分,Kong将提供先前配置的cert.pem证书。

限制客户端协议(HTTP/HTTPS/TCP/TLS)

路由具有protocols属性,以限制他们应该侦听的客户端协议。此属性接受一组值,可以是“http”“https”“tcp”“tls”

具有httphttps的路由将接受两种协议中的流量。

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["http", "https"],
    "service": {
        "id": "..."
    }
}

未指定任何协议具有相同的效果,因为路由默认为[“http”,“https”]

但是,仅使用https的路由接受通过HTTPS的流量。如果以前从受信任的IP发生SSL终止,它也会接受未加密的流量。当请求来自trusted_ip中的一个配置的IP并且如果设置了X-Forwarded-Proto:https header时,SSL终止被认为是有效的:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["https"],
    "service": {
        "id": "..."
    }
}

如果上述路由与请求匹配,但该请求是纯文本而没有有效的先前SSL终止,则Kong响应:

HTTP/1.1 426 Upgrade Required
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: Upgrade
Upgrade: TLS/1.2, HTTP/1.1
Server: kong/x.y.z

{"message":"Please use HTTPS protocol"}

从Kong 1.0开始,可以使用protocols属性中的“tcp”为原始TCP(不一定是HTTP)连接创建路由:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["tcp"],
    "service": {
        "id": "..."
    }
}

同样,我们可以使用“tls”值创建接受原始TLS流量(不一定是HTTPS)的路由:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["tls"],
    "service": {
        "id": "..."
    }
}

仅具有TLS的路由仅接受通过TLS的流量。 也可以同时接受TCP和TLS:

{
    "hosts": ["..."],
    "paths": ["..."],
    "methods": ["..."],
    "protocols": ["tcp", "tls"],
    "service": {
        "id": "..."
    }
}

代理WebSocket流量

由于底层的Nginx实现,Kong支持WebSocket流量。如果希望通过Kong在客户端和上游服务之间建立WebSocket连接,则必须建立WebSocket握手。这是通过HTTP升级机制完成的。这是您的客户要求对Kong的看法:

GET / HTTP/1.1
Connection: Upgrade
Host: my-websocket-api.com
Upgrade: WebSocket

这将使Kong将ConnectionUpgrade header 转发到您的上游服务,而不是由于标准HTTP代理的逐跳特性而将其解除。

WebSocket和TLS

Kong将在其各自的httphttps端口接受wswss连接。要从客户端强制执行TLS连接,请将Route的protocols属性设置为https

将 Service 设置为指向上游WebSocket服务时,应仔细选择要在Kong和上游之间使用的协议。如果要使用TLS(wss),则必须使用服务protocol属性中的https协议和正确的端口(通常为443)定义上游WebSocket服务。要在没有TLS(ws)的情况下进行连接,则应在协议中使用http协议和端口(通常为80)。

如果您希望Kong终止SSL/TLS,您只能从客户端接受wss,而是通过纯文本或ws代理上游服务。

结论

通过本指南,我们希望您了解Kong的基础代理机制,从请求如何匹配路由到其关联的服务,到如何允许使用WebSocket协议或设置动态SSL证书。

该网站是开源的,可以在 https://github.com/Kong/docs.konghq.com 找到。 如果您还没有,我们建议您还阅读负载平衡参考,因为它与我们刚刚介绍的主题密切相关。

在 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/entities-cache/ (不能保证所有的翻译都是准确无误的,所有如有翻译的不准确或错误之处,请一定记得查看原文,并欢迎留言指出)。

介绍

Kong的自定义插件由Lua源文件组成,这些源文件需要位于每个Kong节点的文件系统中。本指南将为您提供逐步说明,使Kong节点了解您的自定义插件。这些步骤应该应用于Kong集群中的每个节点,以确保每个节点上都有自定义插件。

打包源

您可以使用常规打包策略(例如tar),也可以使用LuaRocks包管理器为您执行此操作。我们推荐使用LuaRocks,因为它在使用其中一个官方分发包时与Kong一起安装。

使用LuaRocks时,您必须创建一个rockspec文件,用来指定包的内容。有关示例,请参阅Kong插件模板,有关该格式的更多信息,请参阅有关rockspecs的LuaRocks文档

使用以下命令打包你的rock(来自插件仓库):

# install it locally (based on the `.rockspec` in the current directory)
$ luarocks make

# 打包已安装的rock
$ luarocks pack <plugin-name> <version>

假设你的插件rockspec的名字为kong-plugin-myPlugin-0.1.0-1.rockspec,上面就会变成:

$ luarocks pack kong-plugin-myPlugin 0.1.0-1

LuaRocks pack命令现在已经创建了一个.rock文件(这只是一个包含安装rock所需内容的zip文件)。

如果您不使用或不能使用LuaRocks,则使用tar将插件所包含的.lua文件打包到.tar.gz存档中。 如果目标系统上有LuaRocks,也可以包含.rockspec文件。

该插件的内容应该接近以下内容:

$ tree <plugin-name>
<plugin-name>
├── INSTALL.txt
├── README.md
├── kong
│   └── plugins
│       └── <plugin-name>
│           ├── handler.lua
│           └── schema.lua
└── <plugin-name>-<version>.rockspec

安装插件

要使Kong节点能够使用自定义插件,必须在主机的文件系统上安装自定义插件的Lua源。有多种方法:通过LuaRocks,或手动。 选择一个,然后跳转到第3部分。

  1. 来自新建的’rock’的LuaRocks。
    .rock文件是一个自包含的软件包,可以在本地安装,也可以从远程服务器安装。
    如果您的系统中安装了luarocks实用程序(如果使用其中一个官方安装包,可能就是这种情况),您可以在LuaRocks树(LuaRocks安装Lua模块的目录)中安装“rock”。
    它可以通过以下方式安装:

     $ luarocks install <rock-filename>
    

    文件名可以是本地名称,或任何支持的方法。
    例如:http://myrepository.lan/rocks/myplugin-0.1.0-1.all.rock

  2. 从源档案中通过LuaRocks安装。 如果您的系统中安装了luarocks实用程序(如果使用其中一个官方安装包,可能就是这种情况),您可以在LuaRocks树(LuaRocks安装Lua模块的目录)中安装Lua源代码。
    您可以通过将当前目录更改为提取的存档来实现,其中rockspec文件是:

     $ cd <plugin-name>
    

    然后运行以下命令:

     $ luarocks make
    

    这将在系统的LuaRocks树中的kong/plugins/<plugin-name>中安装Lua源代码,其中所有的Kong源都已存在。

  3. 手动
    安装插件源的一种更保守的方法是避免“污染”LuaRocks树,而是将Kong指向包含它们的目录。
    这是通过调整Kong配置的lua_package_path属性来完成的。如果你熟悉它,那么这个属性是Lua VM的LUA_PATH变量的别名。
    这些属性包含以分号分隔的目录列表,用于搜索Lua源。它应该在您的Kong配置文件中设置如下:

     lua_package_path = /<path-to-plugin-location>/?.lua;
    

    继续:
    4./<path-to-plugin-location>是包含提取的存档的目录的路径。它应该是归档中kong目录的位置。
    5.?是一个占位符,将被kong.plugins替换。<plugin-name>当Kong将尝试加载你的插件。
    6.;;“默认Lua路径”的占位符。不要改变它。
    例如:
    插件位于文件系统上,使处理程序文件为:

     /usr/local/custom/kong/plugins/<something>/handler.lua
    

    kong目录的位置是:/usr/local/custom,因此正确的路径设置将是:

     lua_package_path = /usr/local/custom/?.lua;;
    

    多个插件:

    如果您希望以这种方式安装两个或更多自定义插件,可以将变量设置为:

      lua_package_path = /path/to/plugin1/?.lua;/path/to/plugin2/?.lua;;
    
     7.`;`是目录之间的分隔符。
     8.`;;`仍然意味着“默认的Lua路径”。
    

    注意:您还可以通过其等效的环境变量KONG_LUA_PACKAGE_PATH设置此属性。

提醒:无论您使用哪种方法来安装插件的源,您仍必须为Kong群集中的每个节点执行此操作。

加载插件

您现在必须将自定义插件的名称添加到Kong配置中的插件列表中(在每个Kong节点上):

plugins = bundled,<plugin-name>

或者,如果您不想包含默认捆绑的插件:

plugins = <plugin-name>

或者

plugins = plugin1,plugin2

注意:您还可以通过其等效的环境变量KONG_PLUGINS来设置此属性。 提醒:不要忘记更新Kong群集中每个节点的plugins指令。 提醒:插件重启后会生效:

kong restart

但是,如果你想在kong永不停止时应用插件,你可以使用:

kong prepare
kong reload

验证加载插件

你现在应该能够毫无问题地启动Kong。 请参阅自定义插件有关如何在服务,路由或消费者实体上启用/配置插件的说明。

为确保您的插件由Kong加载,您可以使用调试日志级别启动Kong:

log_level = debug

或者

KONG_LOG_LEVEL=debug

然后,您应该看到正在加载的每个插件的以下日志:

[debug] Loading plugin <plugin-name>

删除插件

完全删除插件有三个步骤。

  1. 从您的Kong Service或Route配置中删除插件。确保它不再适用于全局,也不适用于任何服务,路由或使用者。对于整个Kong集群,只需执行一次,不需要重新启动/重新加载。此步骤本身将使插件不再使用。但它仍然可用,仍然可以重新应用插件。
  2. plugins指令中删除插件(在每个Kong节点上)。确保在执行此操作之前已完成步骤1。在此步骤之后,任何人都无法将插件重新应用于任何Kong Service,Route,Consumer甚至全局。此步骤需要重新启动/重新加载Kong节点才能生效。
  3. 要彻底删除插件,请从每个Kong节点中删除与插件相关的文件。在删除文件之前,请确保已完成步骤2,包括重新启动/重新加载Kong。如果你使用LuaRocks来安装插件,你可以使用luarocks remove <plugin-name>来删除它。

分发插件

这样做的首选方法是使用LuaRocks,Lua模块的包管理器。它称这些模块为“rocks”。 您的模块不必存在于Kong存储库中,但如果您希望维护Kong设置,则可能就是这样。

通过在rockspec文件中定义模块(及其最终依赖项),您可以通过LuaRocks在您的平台上安装这些模块。

您也可以在LuaRocks上传模块并将其提供给所有人!

有关示例,请参阅Kong插件模板,有关该格式的更多信息,请参阅有关rockspecs的LuaRocks文档

故障排除

由于以下几个原因,配置错误的自定义插件可能无法启动:

  • “plugin is in use but not enabled” -> 您从另一个节点配置了一个自定义插件,并且该插件配置在数据库中,但您尝试启动的当前节点在其plugins指令中没有它。要解决此问题,请将插件的名称添加到节点的plugins指令中。
  • “plugin is enabled but not installed” -> 插件的名称出现在plugins指令中,但是Kong无法从文件系统加载handler.lua源文件。要解决此问题,请确保正确设置lua_package_path指令以加载此插件的Lua源。
  • “no configuration schema found for plugin” -> 插件已在plugins指令中安装,但是Kong无法从文件系统加载schema.lua源文件。要解决此问题,请确保schema.lua文件与插件的handler.lua文件一起存在。