
API网关后端云原生【免费下载链接】tykOpen Source API and AI Gateway supporting REST, GraphQL, TCP, gRPC and MCP (Model Context Protocol)项目地址https://gitcode.com/gh_mirrors/ty/tyk点击查看免费下载本文以 Tyk 开源网关仓库中的 ci/smoke-tests/README.md 为骨架结合smoke-tests/plugin-aliasing目录下的真实测试脚本、插件源码、API 定义与 docker-compose 编排系统讲解 Tyk 冒烟测试的组织规范并深入剖析插件别名plugin aliasing场景如何验证 Go 插件编译器、插件的多 API 复用以及网关侧版本感知的.so插件加载机制。读完本文你将能独立为 Tyk 编写一个以退出码报告成败的冒烟测试并理解 goplugin 从编译到挂载再到运行的完整链路。一、什么是 Tyk 的冒烟测试它与常规 CI 测试有何区别在 Tyk 仓库中ci/smoke-tests/是一个特殊的测试目录。README.md 开篇就明确了它的定位The tests under this directory (smoke-tests/) are smoke tests maintained by a squads to test a very specific functionality during the build. Regular tests that should be part of the ci process should go toci/tests/也就是说smoke-tests 由各个 squad功能团队维护用于在构建过程中验证某个非常具体的功能是否可用而应当纳入常规 CI 流程的回归测试、单元测试等应放到ci/tests/目录。两者分工明确目录定位维护方ci/smoke-tests/构建期冒烟验证聚焦单一特定功能各功能 squadci/tests/常规 CI 测试回归、集成等全体开发这种拆分保证了冒烟测试轻量、快速、聚焦不会因覆盖全部功能而拖慢构建反馈。二、如何编写一个冒烟测试三条约定README 用最简练的方式给出了冒烟测试的编写规范总共三条为你的测试创建一个子目录sub-directory测试相关文件全部收敛在该目录内在该目录中实现一个test.sh脚本脚本负责运行测试并通过退出码exit code报告失败——非零退出码即视为测试失败如有必要在本 README 中记录该测试说明它验证什么、如何运行。仓库现状完全遵循这一约定ci/smoke-tests/下仅有plugin-aliasing/一个测试目录其中包含test.sh、docker-compose.yml以及foobar-plugin/、helloworld-plugin/两个插件子目录目录结构如下ci/smoke-tests/ ├── README.md # 本说明文档编写规范 plugin-aliasing 简介 └── plugin-aliasing/ ├── test.sh # 冒烟测试入口脚本 ├── docker-compose.yml # 网关 Redis httpbin 编排 ├── foobar-plugin/ │ ├── main.go # 添加 Foo: Bar 请求头的插件 │ ├── go.mod / go.sum │ ├── README.md │ ├── foobar-plugin-1.json # 挂载 foobar 插件的 API 定义 1 │ └── foobar-plugin-2.json # 挂载 foobar 插件的 API 定义 2 └── helloworld-plugin/ ├── main.go # 添加 Hello: World 请求头的插件 ├── go.mod / go.sum ├── README.md ├── helloworld-plugin-1.json └── helloworld-plugin-2.json这条约定同样适用于你后续新增的任何冒烟测试一个目录 一个test.sh 必要的资源文件就构成了最小完备的冒烟测试单元。三、plugin-aliasing 测试验证什么README 对plugin-aliasing测试的目标描述得非常明确它是本文的核心案例使用合适的plugin-compiler插件编译器编译foobar-plugin/main.go与helloworld-plugin/main.go两个 Go 插件将foobar-plugin与helloworld-plugin下的API 定义挂载进apps/网关的 API 配置目录用2 个 API 挂载 foobar 插件、2 个 API 挂载 helloworld 插件从而同时验证两件事插件编译器可以一次编译多个插件foobar 与 helloworld 分别独立编译成功同一个插件可以被多个 API 复用同一份.so被两个 API 定义同时引用并生效。两个插件的行为非常直观foobar 插件为所有请求添加请求头Foo: Barhelloworld 插件为所有请求添加请求头Hello: World。因此验证方式就是在网关暴露的 4 个端点上分别发起请求断言响应回显的请求头是否包含对应值。由于 httpbin 的/headers接口会回显收到的请求头测试可以非常方便地进行断言。四、插件源码剖析两个插件的源码几乎同构均位于仓库 ci/smoke-tests/plugin-aliasing/foobar-plugin/main.go 与 ci/smoke-tests/plugin-aliasing/helloworld-plugin/main.go。以 foobar 为例package main import ( html/template net/http // Example of package with different version in go.mod github.com/Masterminds/sprig/v3 // Example of package which is not part of Gateway github.com/kr/pretty github.com/TykTechnologies/tyk/ctx github.com/TykTechnologies/tyk/log ) var logger log.Get() // AddFooBarHeader adds custom Foo: Bar header to the request // //nolint:deadcode func AddFooBarHeader(rw http.ResponseWriter, r *http.Request) { r.Header.Add(Foo, Bar) logger.Info(Test) api : ctx.GetDefinition(r) if api ! nil { logger.Info(API Definition, pretty.Sprint(api)) } // Set up variables and template. tpl : Hello {{.Name | trim | lower}} // Get the Sprig function map. template.Must(template.New(test).Funcs(sprig.FuncMap()).Parse(tpl)) } func main() {}这段代码有几个值得注意的技术点导出函数即插件入口AddFooBarHeader(rw http.ResponseWriter, r *http.Request)是导出函数签名与标准http.HandlerFunc一致这正是 Tyk goplugin 约定识别的函数形态。main函数为空仅用于满足 Go 可执行程序的要求。插件可以依赖网关内部包插件导入了github.com/TykTechnologies/tyk/ctx与github.com/TykTechnologies/tyk/log分别用于通过ctx.GetDefinition(r)获取当前请求对应的 API 定义并借助kr/pretty格式化打印以及获取网关日志器输出日志。这说明插件编译时与网关共享同一套 API 上下文插件代码可以访问请求上下文中的网关数据。依赖多样性验证注释明确指出sprig/v3是一个与 go.mod 中版本不同的包示例kr/pretty是不属于网关的包示例——它们在编译期被解析并打进.so用于验证插件编译器能正确处理网关外部依赖。模板函数可用性代码用sprig.FuncMap()注册了 Sprig 模板函数并解析了一个模板Hello {{.Name | trim | lower}}虽然没有实际渲染输出但确保插件在加载阶段不会因模板初始化失败而崩溃。对应 go.mod 内容如下module github.com/TykTechnologies/tyk/smoke-tests/plugin-compiler/foobar-plugin go 1.22 require github.com/kr/pretty v0.3.1 // indirect插件模块声明为go 1.22只显式 require 了kr/pretty其余依赖sprig、ctx、log由 go.mod 的传递依赖解析。helloworld 插件的差异仅在函数名与注入的请求头AddHelloWorldHeader执行r.Header.Add(Hello, World)其余逻辑完全一致。两个插件各自维护独立的 go.mod / go.sum互不干扰。五、编译阶段如何使用 plugin-compiler 编译插件测试脚本 ci/smoke-tests/plugin-aliasing/test.sh 是整条链路的核心它完成准备镜像 → 编译插件 → 启动网关 → 请求断言四步。先看它的镜像准备与编译部分#!/bin/bash set -eo pipefail function setup { local tag${1:-v0.0.0} # Setup required env vars for docker compose export GATEWAY_IMAGE${GATEWAY_IMAGE:-tykio/tyk-gateway:${tag}} export PLUGIN_COMPILER_IMAGE${PLUGIN_COMPILER_IMAGE:-tykio/tyk-plugin-compiler:${tag}} docker pull -q $GATEWAY_IMAGE || true docker pull -q $PLUGIN_COMPILER_IMAGE || true } setup $1要点说明脚本接收一个版本参数./test.sh version默认值为v0.0.0README 强调该版本需在 Docker Hub 上可用因为脚本会用docker pull拉取对应 tag 的tykio/tyk-gateway与tykio/tyk-plugin-compiler镜像。两个镜像均可通过环境变量GATEWAY_IMAGE、PLUGIN_COMPILER_IMAGE覆盖便于在本地构建镜像后直接注入测试。set -eo pipefail保证任一步骤失败都会立即中断脚本并返回非零退出码——这正是用退出码报告失败约定的落地实现。随后脚本提取网关版本并清理旧的编译产物GATEWAY_VERSION$(docker run --rm -t $GATEWAY_IMAGE --version 21) GATEWAY_VERSION$(echo $GATEWAY_VERSION | perl -n -e/(\d).(\d).(\d)/ print v$1\.$2\.$3) rm -rfv foobar-plugin/*.so helloworld-plugin/*.so这里用docker run ... --version从网关镜像中读出版本号再用 perl 正则提取出vX.Y.Z形式的主版本三元组——该版本号稍后会被拼进插件文件名是版本感知插件命名的关键输入。接下来是两个插件的编译命令docker volume create plugin-aliasing-go-mod-cache docker volume create plugin-aliasing-go-build-cache cache_args-v plugin-aliasing-go-mod-cache:/go/pkg/mod -v plugin-aliasing-go-build-cache:/root/.cache/go-build docker run --rm -e GO_GET1 $cache_args -v pwd/foobar-plugin:/plugin-source $PLUGIN_COMPILER_IMAGE foobar-plugin.so docker run --rm -e GO_GET1 $cache_args -v pwd/helloworld-plugin:/plugin-source $PLUGIN_COMPILER_IMAGE helloworld-plugin.so要点说明两个插件分别独立编译通过-v $(pwd)/foobar-plugin:/plugin-source将插件源码目录挂载进 plugin-compiler 容器容器输出.so文件写回宿主机插件目录产物文件名由最后一个参数指定foobar-plugin.so/helloworld-plugin.so。-e GO_GET1指示编译器允许在编译过程中通过go get拉取依赖通过两个命名 volume 缓存 Go 模块与构建缓存加速重复构建也体现了 CI 场景下的工程细节。编译完成后脚本把插件参数导出为环境变量供 docker-compose 挂载使用# if params were not sent, then attempt to get them from env vars if [[ $GOOS ]] [[ $GOARCH ]]; then GOOS$(go env GOOS) GOARCH$(go env GOARCH) fi # pass plugin params export plugin_version${GATEWAY_VERSION} export plugin_os${GOOS} export plugin_arch${GOARCH} docker compose up -d --wait --force-recreate || { docker compose logs gw; exit 1; }plugin_version取自网关镜像版本plugin_os/plugin_arch默认取本机go env的 GOOS/GOARCH也可由外部环境变量覆盖。docker compose up -d --wait --force-recreate启动并等待服务就绪启动失败则输出网关日志并退出。六、编排与加载docker-compose 如何把插件挂进网关ci/smoke-tests/plugin-aliasing/docker-compose.yml 定义了三个服务redis网关依赖的存储、gw被测网关、httpbin.org上游回显服务使用kennethreitz/httpbin镜像。网关服务的关键挂载如下gw: image: ${GATEWAY_IMAGE} volumes: - ./foobar-plugin/foobar-plugin_${plugin_version}_${plugin_os}_${plugin_arch}.so:/opt/tyk-gateway/middleware/foobar-plugin.so - ./helloworld-plugin/helloworld-plugin_${plugin_version}_${plugin_os}_${plugin_arch}.so:/opt/tyk-gateway/middleware/helloworld-plugin.so - ./helloworld-plugin/helloworld-plugin-1.json:/opt/tyk-gateway/apps/helloworld-plugin-1.json - ./helloworld-plugin/helloworld-plugin-2.json:/opt/tyk-gateway/apps/helloworld-plugin-2.json - ./foobar-plugin/foobar-plugin-1.json:/opt/tyk-gateway/apps/foorbar-plugin-1.json - ./foobar-plugin/foobar-plugin-2.json:/opt/tyk-gateway/apps/foorbar-plugin-2.json ports: - 0.0.0.0:8080:8080 environment: - TYK_LOGLEVELdebug - TYK_DB_REDISHOSTredis这里有两类挂载含义各不相同.so插件文件宿主机上按{插件名}_{plugin_version}_{plugin_os}_{plugin_arch}.so命名例如foobar-plugin_v5.0.0_linux_amd64.so的产物被挂载到容器内的普通文件名/opt/tyk-gateway/middleware/foobar-plugin.so。也就是说docker-compose 负责把版本感知命名的文件重命名为 API 定义中引用的路径。API 定义 JSON4 个 API 定义被挂载进网关的apps/目录网关启动时自动加载这些 API。同时网关以TYK_LOGLEVELdebug输出调试日志TYK_DB_REDISHOSTredis指向同栈的 Redis 服务端口 8080 暴露到宿主机供测试请求。七、API 定义一个插件如何挂到多个 API 上以 foobar-plugin-1.json 为例API 定义中与插件相关的核心片段是custom_middleware块custom_middleware: { pre: [], post: [ { name: AddFooBarHeader, path: /opt/tyk-gateway/middleware/foobar-plugin.so, require_session: false } ], post_key_auth: [], auth_check: {}, response: [], driver: goplugin, id_extractor: { extract_from: , extract_with: , extractor_config: {} } }关键字段说明driver: goplugin声明中间件驱动为 Go 插件post数组定义 post 阶段执行的 Go 插件中间件name对应插件导出的函数名AddFooBarHeaderpath指向容器内.so文件路径require_session: false表示该中间件不要求会话配合use_keyless: true的无鉴权模式其余中间件槽位pre、post_key_auth、auth_check、response均为空测试聚焦 post 阶段的头注入。API 定义的其余部分采用 keyless 直通配置use_keyless: true、use_go_plugin_auth: falseproxy块将listen_path设为/goplugin-foobar-1/第二个 API 为/goplugin-foobar-2/target_url指向http://httpbin.org/strip_listen_path: true。对比 helloworld-plugin-1.json 与 helloworld-plugin-2.json结构完全一致仅name换为AddHelloWorldHeader、path指向helloworld-plugin.so、listen_path为/goplugin-helloworld-1//goplugin-helloworld-2。两个插件各被两份 API 定义引用且每个插件只编译出一份.so——这正是同一插件可被多个 API 复用的验证载体。八、运行与验证curl jq 断言启动就绪后脚本对 4 个端点逐一发起请求并断言响应头curl -vvv http://localhost:8080/goplugin-helloworld-1/headers curl http://localhost:8080/goplugin-helloworld-1/headers | jq -e .headers.Hello World || { docker compose logs gw; exit 1; } curl -vvv http://localhost:8080/goplugin-helloworld-2/headers curl http://localhost:8080/goplugin-helloworld-2/headers | jq -e .headers.Hello World || { docker compose logs gw; exit 1; } curl -vvv http://localhost:8080/goplugin-foobar-1/headers curl http://localhost:8080/goplugin-foobar-1/headers | jq -e .headers.Foo Bar || { docker compose logs gw; exit 1; } curl -vvv http://localhost:8080/goplugin-foobar-2/headers curl http://localhost:8080/goplugin-foobar-2/headers | jq -e .headers.Foo Bar || { docker compose logs gw; exit 1; }断言逻辑说明请求经网关转发到 httpbin/headers返回 JSON 格式的请求头回显jq -e .headers.Hello World在断言成立时输出true并以 0 退出断言失败则以非零退出码触发||分支打印网关日志并exit 14 组断言覆盖了两个插件 × 各两个 API的全部组合只要任意一个 API 上的插件头注入失效例如.so未加载、插件符号找不到、API 定义未生效测试即失败。脚本末尾还有一处工程细节trap docker compose down EXIT无论测试成功还是失败退出时都会通过 trap 自动拆除 docker compose 环境避免 CI 机器上残留容器污染后续任务。九、网关侧机制版本感知的插件加载源码佐证为什么.so文件要按{插件名}_{版本}_{OS}_{架构}.so命名这与网关的插件加载机制直接相关。在 gateway/mw_go_plugin.go 中GoPluginMiddleware负责加载并执行 Go 插件其loadPlugin()的注释明确描述了加载策略loadPlugin loads the plugin file from m.Path, it will try converting it to the tyk version aware format:{plugin_name}_{tyk_version}_{os}_{arch}.so... later, it will try with m.path which can be{plugin_name}.so即网关会优先尝试加载版本感知命名的.so找不到再回退到 API 定义中给出的原始路径。命名格式的具体实现在 goplugin/plugin_name_builder.go// getPluginNameFromTykVersion builds a name of plugin based on tyk version, // GOOS, and GOARCH of the build. The structure of the plugin name looks like: // {plugin-dir}/{plugin-name}_{GW-version}_{OS}_{arch}.so func getPluginNameFromTykVersion(version string, pluginPath string) string { ... // produce a name_{version}_{goos}_{goarch} for loading newPluginName : strings.Join([]string{pluginName, version, runtime.GOOS, runtime.GOARCH}, _) newPluginPath : pluginDir / newPluginName .so return newPluginPath }而 GetPluginFileNameToLoad 依次尝试三种形态新版命名格式且版本号带v前缀{name}_{v版本}_{goos}_{goarch}.so新版命名格式但版本号不带v前缀直接使用 API 定义中给出的原始路径例如foobar-plugin.so。此外getPrefixedVersion()会为版本号补v前缀并清理-rc15之类的后缀确保插件名中的版本段是纯净的vX.Y.Z。回到 smoke-test 场景这条机制就形成了一条完整自洽的闭环测试脚本用plugin_version、plugin_os、plugin_arch拼接出版本感知文件名供 docker-compose 挂载docker-compose 将其重命名为 API 定义引用的普通路径网关启动时loadPlugin()先按版本感知命名查找容器内不存在再回退到普通路径命中挂载文件。同时插件文件名中的版本、OS、架构与网关构建信息一一对应从机制上规避了插件与网关版本/平台不匹配导致的加载失败。十、小结新增一个冒烟测试的 Checklist综合 README 约定与 plugin-aliasing 的工程实践为 Tyk 新增冒烟测试时可遵循以下清单建目录在ci/smoke-tests/下创建以功能命名的子目录写test.sh脚本必须以非零退出码报告失败推荐set -eo pipefail 关键步骤|| exit 1必要时用trap清理环境准备资源插件源码、API 定义 JSON、docker-compose 编排等全部收敛在测试目录内文档化在 ci/smoke-tests/README.md 中补充一段该测试验证什么、如何运行的说明遵循职责边界冒烟测试只覆盖构建期需快速验证的特定功能需要长期回归的测试应放入ci/tests/理解加载约定.so命名遵循{插件名}_{网关版本}_{GOOS}_{GOARCH}.so与网关 goplugin/plugin_name_builder.go 的加载逻辑保持一致。plugin-aliasing 作为 Tyk 仓库中冒烟测试的样板案例用不到百行脚本加 4 份 API 定义就完整验证了多插件编译、单插件多 API 复用、版本感知加载三个关键能力是理解 Tyk goplugin 体系从构建到运行时全链路的最佳起点。赞分享API网关后端云原生【免费下载链接】tykOpen Source API and AI Gateway supporting REST, GraphQL, TCP, gRPC and MCP (Model Context Protocol)项目地址https://gitcode.com/gh_mirrors/ty/tyk点击查看免费下载相关推荐Mastra Server 部署验证测试指南--test server 从部署到 API 冒烟验证Mastra Server 部署验证测试指南 test server 从部署到 API 冒烟验证 本指南是 Mastra 项目冒烟测试体系中 server 专人工智能Agent 框架AI AgentRAG后端Mastra MCP 冒烟测试指南从 Studio 页面检查到 /api/mcp API 验证--test mcpMastra MCP 冒烟测试指南从 Studio 页面检查到 /api/mcp API 验证 test mcp 导读 本文围绕 Mastra 仓库中人工智能Agent 框架AI AgentRAG后端Deep-TEMPEST 快速上手指南Conda 与 Pyenv 两种环境搭建全流程Deep TEMPEST 快速上手指南Conda 与 Pyenv 两种环境搭建全流程 Deep TEMPEST 是一个利用深度学习从 HDMI 电磁辐射中恢复创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考