Zeros Tech Zeros Tech
首页
架构
大数据
数据库
  • 面试

    • Java面试
    • 大数据面试
    • 架构面试
语言
运维
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

迹_Jason

全栈工程师
首页
架构
大数据
数据库
  • 面试

    • Java面试
    • 大数据面试
    • 架构面试
语言
运维
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 面试

  • 算法

  • 基础

  • 中间件

    • APISIX
      • 待需要整理内容
      • 概念
        • 重要文件说明
        • 重要接口
        • 优先级
        • 环境要求
        • Docker 使用
      • 插件
        • Java Plugin Runner
        • Open-Id Connect(OIDC)
        • 自定义插件
        • 插件调试
      • 配置
      • Lua
        • 语法
      • Nginx::TEST
      • 问题与解决方案
    • Skywalking
  • Swagger

  • 技术
  • 中间件
迹_Jason
2021-09-16

APISIX

# APISIX

[TOC]

# 待需要整理内容

  • [ ] 如何编写插件
    • [ ] 插件执行优先级
  • [ ] 整体架构情况
  • [ ] 如何实现灰度部署
    • [ ] 编写灰度部署应用方案及存在的问题
  • [ ] 限流
  • [ ] 是否存在服务降级策略
  • [ ] 权限认证
    • [ ] 关注自定义实现
  • [ ] 多租户,数据隔离
  • [ ] APISIX 性能情况及其原因
  • [ ] 更新状态,是否存在阻塞

# 概念

  • 路由:定义一些规则来匹配客户端的请求,然后根据匹配结果加载并执行相应的 插件,并把请求转发给到指定 Upstream。
  • 脚本:Script 配置可直接绑定在 Route 上。Script与Plugin互斥,且优先执行Script。
  • 服务:Service 是某类 API 的抽象(也可以理解为一组 Route 的抽象)。它通常与上游服务抽象是一一对应的,Route 与 Service 之间,通常是 N:1 的关系,Service中包含插件配置信息。
  • 消费者:Consumer 是某类服务的消费者,需与用户认证体系配合才能使用。 比如不同的 Consumer 请求同一个 API,网关服务根据当前请求用户信息,对应不同的 Plugin 或 Upstream 配置。
  • Upstream 是虚拟主机抽象,对给定的多个服务节点按照配置规则进行负载均衡。

# 重要文件说明

  • config.yaml 为配置文件
  • apisix/deps/share/lua/5.1 该目录下面是openrest依赖信息

# 重要接口

  • control API:https://apisix.apache.org/zh/docs/apisix/control-api/#get-v1schema
  • Admin API:https://apisix.apache.org/zh/docs/apisix/admin-api

# 优先级

  • 当 Route 和 Service 都开启同一个插件时,Route 参数的优先级是高于 Service 的
  • Plugin 配置选择优先级总是 Consumer > Route > Service

# 环境要求

ETCD:>3.4.0

# Docker 使用

docker-compose -f docker-compose.yml -p docker-apisix up -d
1

# 插件

# Java Plugin Runner

https://apisix.apache.org/zh/blog/2021/06/21/use-Java-to-write-Apache-APISIX-plugins/

# Open-Id Connect(OIDC)

https://www.cnblogs.com/linianhui/p/openid-connect-core.html

keycloak (opens new window)

ORY Hydra (opens new window)

# 自定义插件

local plugin_name = "example-plugin"

local _M = {
    version = 0.1,
    priority = 0,
    name = plugin_name,
    schema = schema,
    metadata_schema = metadata_schema,
    type = 'auth', #插件类型
    run_policy = 'prefer_route', #用来控制插件执行。当这个字段设置成 prefer_route 时,且该插件同时配置在全局和路由级别,那么只有路由级别的配置生效。
}
1
2
3
4
5
6
7
8
9
10
11

Relation:https://apisix.apache.org/zh/docs/apisix/plugin-develop/

# 插件调试

# 环境准备

环境准备:https://apisix.apache.org/zh/docs/apisix/install-dependencies#mac-osx

环境准备:Mac环境下

brew install lua51
brew install openresty
brew install openresty-openssl111
brew install luarock34
brew install cpanminus
# 源码环境下
cd apisix
git clone https://github.com/iresty/test-nginx.git
rm -rf test-nginx/.git
sudo cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
export PERL5LIB=.:$PERL5LIB
make deps
1
2
3
4
5
6
7
8
9
10
11
12

Ubuntu 环境参见:http://coding.idealworld.group/2021/09/03/apisix-plugin-development/

测试环境

prove -Itest-nginx/lib -r t/plugin/limit-conn.t
1

状态码说明:

  • https://apisix.apache.org/zh/docs/apisix/debug-function

  • https://githubmemory.com/repo/openresty/test-nginx#user-guide

  • https://apisix.apache.org/zh/docs/apisix/how-to-build#%E6%AD%A5%E9%AA%A44%EF%BC%9A%E8%BF%90%E8%A1%8C%E6%B5%8B%E8%AF%95%E6%A1%88%E4%BE%8B

执行命令:

prove -Itest-nginx/lib -r t/plugin/limit-conn.t
TEST_NGINX_BINARY=/usr/local/bin/openresty prove -Itest-nginx/lib -r t/plugin/auth-dew/redis.t
1
2

一些说明:

如果找不到对应的 Plugin config,该路由上的请求会报 503 错误。

502响应头没有X-APISIX-Upstream-Status 说明是APISIX自身报的,还没有到upstream服务。

关于 cpanm 使用说明

#安装依赖方式
cpan Chocolate::Belgian
#安装依赖方式
sudo perl -MCPAN -e shell
cpan[1]> install Test:LongString
geting started
1
2
3
4
5
6

# 添加插件

在 conf/config-default.yaml 文件的plugins下面添加上新增的插件名称。

# 测试

如果第一次执行单测的时候遇到报错【Cannot detect source of ....】执行以下命令试一下

export PERL5LIB=.:$PERL5LIB
// 在apisix项目目录下
cd apisix
prove -Itest-nginx/lib -r t/plugin/limit-conn.t
1
2
3
4

# 日志查看

在单元测试的时候,项目目录下面会存在servroot目录,该目录下面存在logs文件,用于查看日志打印查看。

# 配置

  • conf/config.yaml

注意 不要手工修改 APISIX 自身的 conf/nginx.conf 文件,当服务每次启动时,apisix 会根据 config.yaml 配置自动生成新的 conf/nginx.conf 并自动启动服务。

# Lua

# 语法

local core = require("apisix.core")
# 对象转json
core.json.encode(buffers,true)
# 打印日志
core.log.warn(core.json.encode(conf))
# 字符串
local str = [["Lua 教程"]]
# 使用#号表示长度
#conf.white_list
1
2
3
4
5
6
7
8
9

# Nginx::TEST

参考文档:

  • Run Test (opens new window)
  • ❗️❗️❗️Test Nginx 语法详细说明 (opens new window)

example.lua

local core = require("apisix.core")
local pairs = pairs
local type = type
local ngx = ngx
local buffers = {}
local schema = {
    type = "object",
    properties = {
        message = {
            description = "需要打印的日志",
            type = "string"
        }
    },
    required = { "message" },
    minProperties = 1,
}

local plugin_name = "example"

local _M = {
    version = 0.1,
    priority = 412,
    name = plugin_name,
    schema = schema,
}

function _M.check_schema(conf)
    local ok, err = core.schema.check(schema, conf)
    if not ok then
        return false, err
    end
    core.log.info("xxxxxxxx: ", "gjx")
    return true
end

function _M.log(conf, ctx)
    buffers.message = conf.message
    core.log.debug("metadata:",core.json.encode(buffers,true))
end

return _M
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

Example.t

use t::APISIX 'no_plan';

repeat_each(1); -- 重复次数
no_long_string(); -- 默认地,失败的字符串匹配测试将会使用 Test::LongString 模块产生一个错误信息,在 run_tests 之前调用这个函数将会关闭它
no_root_location();
no_shuffle(); -- 在no_shuffle()调用的情况下,测试块的运行顺序与它们在测试文件中出现的顺序完全一致
log_level('debug'); -- 日志级别
run_tests; -- 这个放在最后

__DATA__

=== TEST 1: sanity  -- 用例名称
--- config   -- 用于配置Nginx conf 信息
    location /t {
        content_by_lua_block {
            local plugin = require("apisix.plugins.example")
             local ctx ={
                headers={
                }
            }
            local ok, err = plugin.check_schema({message="gejiax"})
            if not ok then
                ngx.say(err)
            end
            plugin.log({message="gejiax"},ctx)
            ngx.say("done")  -- 打印结果
        }
    }
--- request  -- 调用请求,校验结果
GET /t
--- more_headers -- 头部信息
Authorization: Bearer eyJhbGc
--- response_headers  -- 响应返回头部信息
in: out
--- error_code: 401 -- 状态码
--- response_body  -- 请求结果
done
--- no_error_log  -- 表示会对 nginx 的 error.log 检查,必须没有 EORROR 级别的记录
[error]
--- response_body_like eval  --返回值正则表达式校验
qr/"Access Denied"/
--- error_log eval   --错误日志正则表达式
qr/conf_version: \d+#1,/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

content_by_lua_block 说明

=== TEST 12: Add https endpoint with ssl_verify false
--- config
    location /t {
        content_by_lua_block {
            local t = require("lib.test_admin").test  -- 调用apisix接口的实现,👇就是如何去调用API接口的示例
            local code, body = t('/apisix/admin/routes/1',
                 ngx.HTTP_PUT,
                 [[{
                        "plugins": {
                            "authz-keycloak": {
                                "token_endpoint": "https://127.0.0.1:8443/auth/realms/University/protocol/openid-connect/token",
                                "permissions": ["course_resource#delete"],
                                "client_id": "course_management",
                                "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
                                "timeout": 3000,
                                "ssl_verify": false
                            }
                        },
                        "upstream": {
                            "nodes": {
                                "127.0.0.1:1982": 1
                            },
                            "type": "roundrobin"
                        },
                        "uri": "/hello1"
                }]],
                [[{
                    "node": {
                        "value": {
                            "plugins": {
                                "authz-keycloak": {
                                    "token_endpoint": "https://127.0.0.1:8443/auth/realms/University/protocol/openid-connect/token",
                                    "permissions": ["course_resource#delete"],
                                    "client_id": "course_management",
                                    "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
                                    "timeout": 3000,
                                    "ssl_verify": false
                                }
                            },
                            "upstream": {
                                "nodes": {
                                    "127.0.0.1:1982": 1
                                },
                                "type": "roundrobin"
                            },
                            "uri": "/hello1"
                        },
                        "key": "/apisix/routes/1"
                    },
                    "action": "set"
                }]]
                )

            if code >= 300 then
                ngx.status = code
            end
            ngx.say(body)
        }
    }
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

HTTP 请求

location /t {
        content_by_lua_block {
            local json_decode = require("toolkit.json").decode  -- json 工具类
            local http = require "resty.http"
            local httpc = http.new()
            local uri = "http://127.0.0.1:8090/auth/realms/University/protocol/openid-connect/token"
            local res, err = httpc:request_uri(uri, {
                    method = "POST",
                    body = "grant_type=password&client_id=course_management&client_secret=d1ec69e9-55d2-4109-a3ea-befa071579d5&username=teacher@gmail.com&password=123456",
                    headers = {
                        ["Content-Type"] = "application/x-www-form-urlencoded"
                    }
                })

            if res.status == 200 then
                local body = json_decode(res.body)
                local accessToken = body["access_token"]


                uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello1"
                local res, err = httpc:request_uri(uri, {
                    method = "GET",
                    headers = {
                        ["Authorization"] = "Bearer " .. accessToken,
                    }
                 })

                if res.status == 200 then
                    ngx.say(true)
                else
                    ngx.say(false)
                end
            else
                ngx.say(false)
            end
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

多个location

=== TEST 6: first request timeout
--- config
    location = /aggregate {
        content_by_lua_block {
            local core = require("apisix.core")
            local t = require("lib.test_admin").test
            local code, body = t('/apisix/batch-requests',
                ngx.HTTP_POST,
                [=[{
                    "timeout": 100,
                    "pipeline":[
                    {
                        "path": "/b",
                        "headers": {
                            "Header1": "hello",
                            "Header2": "world"
                        }
                    },{
                        "path": "/c",
                        "method": "PUT"
                    },{
                        "path": "/d"
                    }]
                }]=],
                [=[[
                {
                    "status": 504,
                    "reason": "upstream timeout"
                }
                ]]=]
                )

            ngx.status = code
            ngx.say(body)
        }
    }

    location = /b {
        content_by_lua_block {
            ngx.sleep(1)
            ngx.status = 200
        }
    }
    location = /c {
        content_by_lua_block {
            ngx.status = 201
        }
    }
    location = /d {
        content_by_lua_block {
            ngx.status = 202
        }
    }
--- request
GET /aggregate
--- response_body
passed
--- error_log
timeout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 问题与解决方案

❓ nginx: [emerg] "listen" directive is not allowed here in /Users/gjason/project/apisix-all/apisix/t/servroot/conf/nginx.conf:188

可能是数据格式错误导致 nginx.conf配置错误。

编辑 (opens new window)
上次更新: 2021/12/17, 16:15:07
travis自动部署
Skywalking

← travis自动部署 Skywalking→

最近更新
01
权限
12-17
02
SpringGateway
12-17
03
Spock
12-17
更多文章>
Theme by Vdoing | Copyright © 2021-2021 迹_Jason | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×