必须用tinygo编译,因envoy wasm运行时仅支持wasi abi,标准go依赖不可移植组件;onhttprequestheaders按stream生命周期仅触发一次,非每次请求。

标准 Go 编译器无法生成 Envoy 可加载的 WASM 模块,必须用 TinyGo;OnHttpRequestHeaders 不是每次 HTTP 请求都触发,而是按 stream 生命周期触发一次。
为什么 tinygo build -target=wasi 是唯一可行路径
Envoy 的 WASM 运行时基于 WASI ABI,不支持标准 Go 运行时依赖的 syscall、net、runtime.GC 等组件。用 go build 生成的 wasm 二进制要么链接失败,要么在 Envoy 启动时 panic。
TinyGo 裁剪了运行时,只保留 WASI 兼容的系统调用,并能生成符合 wasi_snapshot_preview1 ABI 的模块。官方文档和 Istio/ASM 实践均只验证过 TinyGo(v0.30+)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 安装后务必运行
tinygo version,确认输出含wasi支持项 - 编译命令必须显式指定
-target=wasi;-target=wasip1或-target=emscripten会导致 Envoy 加载失败并报WASM runtime error: invalid import - 避免使用
-scheduler=threads:WASI 不支持线程,只能用-scheduler=none或默认调度器 - 禁用
log.Printf、time.Now()、os.Getenv()—— 这些在 WASI 下触发unimplemented syscall
OnHttpRequestHeaders 只在 stream 首次收到 headers 时调用
很多人误以为这个函数会为每个 HTTP 请求执行一次,结果发现 header 修改没生效、配置读不到、日志不打印。根本原因是:Envoy WASM Filter 绑定在 HTTP stream 上,而 stream 可能复用(如 HTTP/2 多路复用),OnHttpRequestHeaders 仅在该 stream 的第一个请求头到达时触发。
- 后续同 stream 的请求(包括重试、trailers)不会再次进入此函数
- 想对每个请求做处理,逻辑必须放在
OnHttpRequestBody或OnHttpResponseBody,并配合end_of_stream标志判断完整 payload - 若需读取完整 body,必须手动缓存分片数据;
OnHttpRequestBody会被多次调用,最后一次的end_of_stream == true - 在
OnHttpRequestHeaders中调用proxy.GetConfiguration()返回空?检查 Envoy 配置中是否在typed_config正确注入了 JSON 字符串
proxy.Log 默认不输出到 Envoy 日志,调试必须显式开启
写完 proxy.Log 却在 docker logs envoy 或 journalctl -u envoy 里完全看不到任何输出,不是代码没跑,而是 Envoy 默认关闭 WASM 日志通道。
- 启动 Envoy 时必须加两个参数:
--log-level debug --component-log-level wasm:debug - 在 Envoy 配置的
http_filters对应 WASM filter 块中,typed_config下需明确设置vm_config.code.local.inline或vm_config.code.local.filename,否则proxy.Log无处可写 - 日志内容不会出现在 stdout/stderr,而是走 Envoy 内部 trace sink,所以
tail -f /dev/stdout无效 - 生产环境建议关闭
wasm:debug,仅保留wasm:warning或更低,避免高频日志拖慢性能
配置未生效或返回 503?检查 root_id 和 VM 共享模型
多个 filter 使用同一份 wasm 二进制但行为不一致,或某个 filter 修改 header 后 downstream 没收到,大概率是 root_id 配置错误或 VM 共享方式不匹配。
-
root_id必须与 Go 代码中proxywasm.RegisterHttpPlugin的第一个参数严格一致,否则 Envoy 不会将请求路由给你的逻辑 - 同一
root_id下的所有 filter 实例共享一个 WASM VM 实例;若需隔离状态(如 per-filter 缓存),必须用不同root_id - Envoy 默认采用“每工作线程 + 每 root_id 一个 VM”模型;高并发下若滥用全局变量,可能引发竞态 —— WASM 沙箱内没有 goroutine,所有回调都是单线程串行执行,但不同 stream 可能在不同线程的 VM 中并发运行
- 修改配置后 reload Envoy 不一定生效:WASM filter 的
configuration字段是静态注入的,变更需重启或通过 xDS 动态推送
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










