Day 01 / 共 20 天 · 第 1 周 入门与生命周期
项目全景:Higress 插件 SDK
欢迎来到 wasm-go——higress-group 系列第 5 站,也是收官站。前四站教你网关(Envoy/Istio/Higress/Console)怎么工作,这一站教你怎么给网关写自己的插件。第一天先建立整体印象。
📍 你在 wasm-go 前 10 天的位置(W1 入门与生命周期 · W2 配置与匹配;W3-4 见 D11-20)
D01 全景·
D02 request-block·
D03 ABI·
D04 三级Context·
D05 SetCtx/选项→
D06 配置解析·
D07 RuleMatcher·
D08 运行时匹配·
D09 全局vs规则·
D10 生命周期→
W3-4 请求处理/高级
💡 先用一套类比兜住整门课(后面天天沿用)
把网关(Envoy)想成一座机场,每个 HTTP 请求就是一名过关的旅客。Wasm 插件 = 你装到安检线上的一台"可插拔外挂芯片"——不用拆机场(不改 Envoy C++、不重编译)就能加一道自定义关卡。proxy-wasm ABI = 芯片和机场中控之间的"对讲机协议"(能喊话"给我看这位旅客的登机牌")。host functions = 芯片向机场借的工具(读请求头、发响应、查 Redis)。wasm-go 就是这台芯片的"开发套件":帮你把对讲机和借来的工具都包好,你只管写"看到 X 旅客就拦下"的业务。记住这套机场世界观,后面 20 天都在给这台芯片加功能。
L01
wasm-go 是什么
🤔 痛点:网关功能不够用,你想加一条"自定义规则"
比如"凡是 URL 含
swagger.html 的请求一律拦掉"。没有插件机制时你只能去改 Envoy 的 C++ 源码、重新编译、重启整个网关——门槛高、风险大、还得停机。就像想给机场加一道安检,却要把整座航站楼推倒重盖。💡 本质:SDK = 外挂芯片的"开发套件"
wasm-go 让你不碰网关本体,用 Go 写一小段业务、编译成一枚
.wasm"外挂芯片"插到安检线上即可。SDK 的价值就是把底层那套"对讲机协议+借工具"全封装好,你只写"看到什么就做什么"。wasm-go(模块名 github.com/higress-group/wasm-go,go.mod:1)是 Higress 官方的 Go 语言 Wasm 插件开发 SDK。README.md 开头就说:"This SDK is used to develop the WASM Plugins for Higress in Go."
用生活例子理解"插件 SDK"
想象网关是一台"可以装 App 的手机",Wasm 插件就是"App"(限流、鉴权、改请求…)。但直接对着手机的底层接口写 App 太难了。wasm-go 就是"App 开发套件"——它把底层接口封装成简单的几个函数,你只管写业务逻辑。好比 iOS 开发用 SwiftUI 而不是直接调内核。整个 SDK 只有约 2500 行 Go 代码,非常精悍。
L02
什么是 Wasm 插件
Wasm 是什么?
WebAssembly(Wasm)是一种"可移植的字节码"——你用 Go/Rust/C++ 写代码,编译成
.wasm 文件,它能在一个"沙箱虚拟机"里跑,跨语言、跨平台、还安全隔离。Envoy(网关数据面)内置了 Wasm 虚拟机,能加载 .wasm 插件并在处理请求时调用它们。于是你能用 Go 写插件、扩展网关能力,而不用改 Envoy 的 C++ 源码、不用重新编译网关。这就是"Wasm 插件"的价值:热插拔、多语言、安全沙箱。读法:回想 Envoy 课的 proxy-wasm——那是"网关侧怎么跑 Wasm";wasm-go 是"插件侧怎么用 Go 写 Wasm",两者是同一机制的两端。
L03
和 proxy-wasm 的关系
核心依赖(go.mod):github.com/higress-group/proxy-wasm-go-sdk。wasm-go 建立在 proxy-wasm-go-sdk 之上,再封装一层更友好的 API。
三层关系(记住这个栈):
① proxy-wasm ABI:Envoy 和 Wasm 插件之间的"通信协议"(一堆底层函数约定,如"读一个请求头""发起 HTTP 调用")。
② proxy-wasm-go-sdk:把 ABI 的原始调用包成 Go 函数(
③ wasm-go(本课):再往上封装成"填回调"的极简模型 + 配置解析 + 规则匹配 + HTTP/Redis 客户端。
① proxy-wasm ABI:Envoy 和 Wasm 插件之间的"通信协议"(一堆底层函数约定,如"读一个请求头""发起 HTTP 调用")。
② proxy-wasm-go-sdk:把 ABI 的原始调用包成 Go 函数(
proxywasm.GetHttpRequestHeader 等)。③ wasm-go(本课):再往上封装成"填回调"的极简模型 + 配置解析 + 规则匹配 + HTTP/Redis 客户端。
为什么要三层?
每层都把下一层的复杂度藏起来。直接用 ABI 要处理内存指针、返回码,很痛苦;用 proxy-wasm-go-sdk 已经是 Go 函数了,但仍要自己管生命周期;用 wasm-go 你连生命周期都不用管,填几个回调就行。层层封装 = 越往上越好用。本课主要讲第③层,偶尔下探到第②层看它怎么调 ABI。
图注:你写的业务停在第 ③ 层;一句
proxywasm.GetHttpRequestHeader 会一路下沉到 ① 的 ABI,再由 Envoy 真正执行。📝 举个例子:一句"读路径"要穿几层
你在第 ③ 层写
ctx.GetHttpRequestHeader(":path") → 第 ② 层翻成 proxywasm.GetHttpRequestHeader(":path") → 第 ① 层通过 ABI 向 Envoy 喊话、传内存指针取回字节 → 最终拿到 "/api/swagger.html"。三层各退一步,你只写最上面这一行。L04
目录结构
wasm-go/
go.mod / go.sum 模块与依赖
README.md 构建与使用说明
pkg/ SDK 核心
wrapper/ 主封装:plugin_wrapper(生命周期/SetCtx)、
http_wrapper、cluster_wrapper、redis_wrapper、
request_wrapper、response_wrapper、log_wrapper
matcher/ rule_matcher 规则匹配
iface/ context.go 接口定义(PluginContext/HttpContext)
log/ log.go 日志
tokenusage/ AI Token 用量解析
test/ 单元测试辅助
protos/ protobuf(inject_encoded_data)
examples/ 示例插件
request-block/ http-call/ complex-http-call/
safe-log-http-call/ rebuild-example/
读法:最核心是
pkg/wrapper/(尤其 plugin_wrapper.go 1290 行)。examples/ 是最好的入门材料——明天就从 request-block 这个最简单的例子开始拆。L05
依赖清单
go.mod:5-16 关键依赖:
github.com/higress-group/proxy-wasm-go-sdk // proxy-wasm 底层
github.com/tidwall/gjson // 快速读 JSON(不解析成结构体,按路径取值)
github.com/tidwall/sjson // 快速改 JSON
github.com/tidwall/resp // Redis RESP 协议
github.com/higress-group/gjson_template // 模板
github.com/invopop/jsonschema // 生成 JSON Schema
github.com/google/uuid // 生成 vmID
为什么用 gjson 而不是标准 json?
标准库
encoding/json 要先定义结构体、再反序列化,代码多。gjson 让你直接按路径取值:json.Get("block_urls").Array()——不用定义结构体,读配置飞快。Wasm 环境对性能和体积敏感,gjson 这种"零分配、按需取"的库特别合适。这也是 Higress 全家(上一站的 ai-proxy 也用 gjson/sjson)的一贯选择。L06
和前四站的关系(收官)
五站怎么串起来
Envoy(数据面)跑 Wasm 虚拟机;Istio/Higress(控制面)把插件配置下发;Higress Console(上一站)提供界面配置插件。而这些被配置、被下发、被运行的插件,很多就是用 wasm-go 写的!你在 Console 里看到的 key-auth、ai-proxy、ai-statistics 等插件(上一站 Day 12 加载的那 43 个),底层实现就是 Go + wasm-go 编译的 Wasm。学完这一站,你就能自己写一个插件,走完"写代码 → 编译 → 打镜像 → Console 配置 → 网关运行"的完整闭环。
建议先学完 Envoy(理解 Wasm 怎么在网关里跑)和 Higress(理解插件配置怎么下发)再学本课。
L07
一个插件的一生
① 你写 Go 代码:init() 里 SetCtx 注册回调 + 各阶段处理函数
→ ② 编译:GOOS=wasip1 GOARCH=wasm go build → main.wasm
→ ③ 打镜像:FROM scratch + COPY main.wasm → 推到 OCI 仓库
→ ④ 配置:WasmPlugin CRD 指向 oci:// 镜像(或 Console 界面配)
→ ⑤ 运行:Higress 下发 → Envoy 为每 worker 克隆 Wasm VM
→ ⑥ 请求到来:Envoy 在各阶段调用你的回调(headers/body…)
读法:本课按这条链推进:第 1-2 周讲①(怎么写:生命周期/配置/匹配),第 3 周讲请求处理与外部调用,第 4 周讲高级特性 + ②③④⑤(怎么编译部署)。Day 20 会把整条链走一遍。
👶 小白:插件是不是每来一个请求就重新加载一次 .wasm?那不慢死了?
👨🏫 老师:不会。.wasm 只在插件配置下发时加载一次,Envoy 会为每个 worker 线程克隆一份 Wasm 虚拟机常驻内存(第 ⑤ 步)。之后请求来了只是调用你注册的回调(第 ⑥ 步),不重新加载。就像安检芯片开机后一直在岗,旅客一个个过,不用每人重启一次。
👶 小白:那我写的全局变量,多个请求之间共享吗?
👨🏫 老师:同一个 worker 的 VM 内共享,但不同 worker 各有一份、互不相通——这正是 Day 04「三级 Context」和 Day 10「OnPluginStart」要解决的问题,先记住有这回事。
⚠️ 常见误解:很多人以为"写 Wasm 插件 = 要懂 WebAssembly 底层"。其实用 wasm-go 你几乎碰不到 Wasm 本身——你写的就是普通 Go,只是编译目标换成
wasip1/wasm。真正的门槛在"理解生命周期和配置模型",而不是 Wasm 字节码。L08
今日小结 + 动手
🧠 今天你应该能回答
- wasm-go 是什么?它封装了谁、提供了什么?
- Wasm 插件为什么好(热插拔/多语言/沙箱)?
- ABI → proxy-wasm-go-sdk → wasm-go 三层各是什么?
- wasm-go 写的插件和前四站怎么串成闭环?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/wasm-go
head -30 README.md
head -20 go.mod
ls pkg/ pkg/wrapper/ examples/
wc -l pkg/wrapper/plugin_wrapper.go pkg/matcher/rule_matcher.go
明天预告 · Day 02:第一个插件 request-block——从最简单的例子入手,逐行读
main/init/SetCtx/parseConfig/onHttpRequestHeaders,看一个"拦截含某关键词请求"的插件是怎么炼成的。