熟悉 Apache APISIX 的小伙伴都知道,之前在社区中我们已经支持了 Java 和 Go 语言的 Runner,今天 Apache APISIX Python Runner 也来了,社区中的小伙伴们在开发 Apache APISIX 插件时又多了一种新选择。

Python 语言作为一个解释型的高级编程语言,它语法简洁易上手、代码可读性好 ,在跨平台 、可移植性 、开发效率上都有很好的表现,同时作为一个高级编程语言它的封装抽象程度比较高屏蔽了很多底层细节(例如:GC )让我们在开发的过程中可以更专注应用逻辑的开发。

同时作为一个有 30 年历史的老牌开发语言,它的生态以及各种模块已经非常完善,我们大部分的开发和应用场景都可以从社区中找到很成熟的模块或解决方案。

Python 其他的优点就不再一一赘述,当然它的缺点也比较明显:Python 作为一门解释性语言,相较于 C++ 和 Go 这样的编译型语言,在性能上的差距还是比较大的。

了解:项目架构

apache-apisix-python-runner 这个项目可以理解为 Apache APISIX 和 Python 之间的一道桥梁,通过 Python Runner 可以把 Python 直接应用到 Apache APISIX 的插件开发中,最重要的还是希望让更多对 Apache APISIX 和 API 网关感兴趣的 Python 开发者通过这个项目,更多地了解和使用 Apache APISIX,以下为 Apache APISIX 多语言支持的架构图。

上图左边是 Apache APISIX 的工作流程,右边的 Plugin Runner 是各语言的插件运行器,本文介绍的 apisix-python-plugin-runner 就是支持 Python 语言的 Plugin Runner

在 Apache APISIX 中配置一个 Plugin Runner 时,Apache APISIX 会启动一个子进程运行 Plugin Runner,该子进程与 Apache APISIX 进程属于同一个用户,当我们重启或重新加载 Apache APISIX 时,Plugin Runner 也将被重启。

如果你为一个给定的路由配置了 ext-plugin-* 插件,请求命中该路由时将触发 Apache APISIX 通过 Unix SocketPlugin Runner 发起 RPC 调用。调用分为两个阶段:

  • ext-plugin-pre-req :在执行 Apache APISIX 内置插件(Lua 语言插件)之前
  • ext-plugin-post-req :在执行 Apache APISIX 内置插件(Lua 语言插件)之后

大家可以根据需要选择并配置 Plugin Runner 的执行时机。Plugin Runner 会处理 RPC 调用,在其内部创建一个模拟请求,然后运行多语言编写的插件,并将结果返回给 Apache APISIX。

多语言插件的执行顺序是在 ext-plugin-* 插件配置项中定义的,像其他插件一样,它们可以被启用并在运行中重新定义。

安装:部署测试

基础运行环境:Apache APISIX 2.7、Python 3.6+

Apache APISIX 的安装部署可参考 Apache APISIX 官方文档:如何构建 Apache APISIX (https://github.com/apache/api...)进行部署。

1. 下载安装 Python Runner

$ git clone https://github.com/apache/apisix-python-plugin-runner.git
$ cd apisix-python-plugin-runner
$ make install

2. 配置 Python Runner

  • 开发模式配置
  1. 运行 Python Runner
$ cd /path/to/apisix-python-plugin-runner
$ APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock python3 apisix/main.py start
  1. 修改 Apache APISIX 配置文件
$ vim /path/to/apisix/conf/config.yaml
apisix:
  admin_key:
    - name: "admin"
      key: edd1c9f034335f136f87ad84b625c8f1
      role: admin
ext-plugin:
  path_for_test: /tmp/runner.sock
  • 生产模式配置
  1. 修改 Apache APISIX 配置文件
$ vim /path/to/apisix/conf/config.yaml
apisix:
  admin_key:
    - name: "admin"
      key: edd1c9f034335f136f87ad84b625c8f1
      role: admin
ext-plugin:
  cmd: [ "python3", "/path/to/apisix-python-plugin-runner/apisix/main.py", "start" ]
  1. Python Runner 配置(可选)

如果需要对 Log LevelUnix Domain Socket 环境变量调整可以修改 Runner 的配置文件

$ vim /path/to/apisix-python-plugin-runner/apisix/config.yaml
socket:
  file: $env.APISIX_LISTEN_ADDRESS # Environment variable or absolute path

logging:
  level: debug # error warn info debug

3. 启动 Python Runner

$ cd /path/to/apisix
# Start or Restart
$ ./bin/apisix [ start | restart ]

启动或重启 Apache APISIX 即可,此时 Apache APISIX 和 Python Runner 已经完成配置并启动。

4. 测试 Python Runner

  1. 配置 Apache APISIX 路由及插件信息
# 使用默认demo插件进行测试
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "uri": "/get",
  "plugins": {
    "ext-plugin-pre-req": {
      "conf": [
        { "name": "stop", "value":"{\"body\":\"hello\"}"}
      ]
    }
  },
  "upstream": {
        "type": "roundrobin",
        "nodes": {
            "127.0.0.1:1980": 1
        }
    }
}'
  • plugins.ext-plugin-pre-req.confRunner 插件配置,conf 为数组格式可以同时设置多个插件。
  • 插件配置对象中 name 为插件名称,该名称需要与插件代码文件和对象名称一致。
  • 插件配置对象中 value 为插件配置,可以为 JSON 字符串。

2. 访问验证

$ curl http://127.0.0.1:9080/get -i
HTTP/1.1 200 OK
Date: Fri, 13 Aug 2021 13:39:18 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
host: 127.0.0.1:9080
accept: */*
user-agent: curl/7.64.1
X-Resp-A6-Runner: Python
Server: APISIX/2.7

Hello, Python Runner of APISIX

实践:插件开发

1. 插件目录

/path/to/apisix-python-plugin-runner/apisix/plugins

此目录中的 .py 文件将会被自动加载。

2. 插件示例

/path/to/apisix-python-plugin-runner/apisix/plugins/stop.py
/path/to/apisix-python-plugin-runner/apisix/plugins/rewrite.py

3. 插件格式

from apisix.runner.plugin.base import Base
from apisix.runner.http.request import Request
from apisix.runner.http.response import Response


class Stop(Base):
    def __init__(self):
        """
        Example of `stop` type plugin, features:
            This type of plugin can customize response `body`, `header`, `http_code`
            This type of plugin will interrupt the request
        """
        super(Stop, self).__init__(self.__class__.__name__)

    def filter(self, request: Request, response: Response):
        """
        The plugin executes the main function
        :param request:
            request parameters and information
        :param response:
            response parameters and information
        :return:
        """
        # 在插件中可以通过 `self.config` 获取配置信息,如果插件配置为JSON将自动转换为字典结构
        # print(self.config)

        # 设置响应头信息
        headers = request.headers
        headers["X-Resp-A6-Runner"] = "Python"
        response.headers = headers

        # 设置响应体信息
        response.body = "Hello, Python Runner of APISIX"

        # 设置响应状态码
        response.status_code = 201

        # 通过调用 `self.stop()` 中断请求流程,此时将立即响应请求给客户端
        # 如果未显示调用 `self.stop()` 或 显示调用 `self.rewrite()`将继续将请求
        # 默认为 `self.rewrite()`
        self.stop()

4. 插件规范及注意事项

  • 实现插件对象必须继承 Base
  • 插件必须实现 filter 函数
  • filter 函数参数只能包含 RequestResponse 类对象作为参数
  • Request 对象参数可以获取请求信息
  • Response 对象参数可以设置响应信息
  • self.config 可以获取插件配置信息
  • filter 函数中调用 self.stop() 时将马上中断请求,响应数据。
  • filter 函数中调用 self.rewrite() 时,将会继续请求。

贡献:参与项目

目前 Apache APISIX 各语言的 Runner 还处于早期开发阶段,我们会陆续完善其功能。成功的开源项目离不开大家的参与和贡献,欢迎各位参与 Apache APISIX Runner 的开发,让我们一起共建 Apache APISIX 与各语言的桥梁。具体项目可参考下方:

推荐阅读

作者介绍

帅进超,Apache APISIX PMC

关于 Apache APISIX

Apache APISIX 是一个动态、实时、高性能的开源 API 网关,提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。Apache APISIX 可以帮忙企业快速、安全的处理 API 和微服务流量,包括网关、Kubernetes Ingress 和服务网格等。

全球已有数百家企业使用 Apache APISIX 处理关键业务流量,涵盖金融、互联网、制造、零售、运营商等等,比如美国航空航天局(NASA)、欧盟的数字工厂、中国航信、中国移动、腾讯、华为、微博、网易、贝壳找房、360、泰康、奈雪的茶等。

200 余位贡献者,一同缔造了 Apache APISIX 这个世界上最活跃的开源网关项目。聪明的开发者们!快来加入这个活跃而多样化的社区,一起来给这个世界带来更多美好的东西吧!

03-05 15:17