ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

iii Go SDK 实战:用 iii-example 从零跑通 Hello World Worker 与 HTTP Trigger

iii Go SDK 实战:用 iii-example 从零跑通 Hello World Worker 与 HTTP Trigger iii Go SDK 实战用 iii-example 从零跑通 Hello World Worker 与 HTTP Trigger【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii本篇技术指南围绕仓库中sdk/packages/go/iii-example可运行示例展开讲解如何用 Go 语言编写一个 iii worker注册函数、绑定 HTTP 触发器、通过 WebSocket 与引擎完成一次往返调用并对外提供 HTTP 接口。读完本文你将掌握 iii Go SDK 的最小可用闭环——从启动引擎、运行 worker 到用 curl 触发函数的完整链路同时理解其背后 HTTP 信封envelope协议与连接生命周期原理。一、iii-example 是什么一个独立、可运行的 Go 示例模块sdk/packages/go/iii-example是 iii Go SDK 的可运行示例模块。它与 SDK 本体sdk/packages/go/iii分属两个独立的 Go module通过go.mod中的replace指令引用本地 SDK 源码// sdk/packages/go/iii-example/go.mod module github.com/iii-hq/iii/sdk/packages/go/iii-example go 1.24 require github.com/iii-hq/iii/sdk/packages/go/iii v0.0.0 replace github.com/iii-hq/iii/sdk/packages/go/iii ../iii这个设计有两个关键点本地源码优先replace指向../iii示例直接基于工作区内的 SDK 源码构建不依赖已发布的 module 标签当前版本尚无发布标签。多语言示例对齐该模块刻意镜像了 Rust 侧iii-examplecrate通过iii-sdk { path ../iii }和 Node 侧iii-examplepackage通过iii-sdk: workspace:*的形态保证三种语言 SDK 的示例体验一致。该示例的核心任务在 main.go 的包注释中写得很清楚注册一个函数、绑定 HTTP 触发器、通过 socket 调用一次以演示往返然后持续服务让 HTTP 触发器可以对运行中的引擎发起真实请求。二、运行三步曲引擎、worker、curl原文档给出的运行流程可以完整还原为三个终端操作# 1. 启动引擎另开一个终端 iii --use-default-config # engine on ws://localhost:49134, HTTP on :3111 # 2. 运行 worker go run . # 在 sdk/packages/go/iii-example 目录下执行 # 3. 调用 HTTP 触发器Content-Type 至关重要——引擎只把 JSON body 解析进请求信封 # curl 的 -d 默认是 form-urlencoded 编码 curl -X POST localhost:3111/greet -H Content-Type: application/json -d {name:world} # {message:Hello, world!}其中两点值得展开关于iii --use-default-config的现状说明。README 示例中保留了该命令但根据当前仓库 engine/src/main.rs 中的测试用例use_default_config_is_no_longer_a_flag该 flag 已被移除iii现在会在config.yaml缺失时自动创建文件缺失模式下的 flag 已无存在意义避免绕过 config.yaml 的 worker 添加与 reload watcher 初始化。因此在实际运行中直接执行iii即可启动引擎文档中的--use-default-config可以视为历史写法。默认端口约定。引擎默认的 worker WebSocket 端点是ws://localhost:49134HTTP API 端口是3111。这两个默认值被 Go SDK 明确固化在 constants.go 中// DefaultEngineURL is the worker WebSocket endpoint of a locally-running engine. const DefaultEngineURL ws://localhost:49134该常量与 Node、Rust SDK 文档中使用的地址保持一致是整个 SDK 的默认连接目标。三、逐段拆解示例代码一个完整的 Hello World workeriii-example的核心实现只有 main.go 一个文件约 120 行却覆盖了 worker 的完整生命周期。下面按执行顺序拆解。3.1 定义 HTTP 信封类型// httpRequest 是引擎 HTTP 触发器 ApiRequest 信封的子集。 // 当函数通过 http 触发器被调用时引擎把请求包装为 // { path, method, body, headers, ... }解析后的请求体位于 body 下 // 而不是顶层。 type httpRequest struct { Body greetBody json:body } type greetBody struct { Name string json:name } // httpResponse 是引擎 HTTP 触发器 ApiResponse 信封 // 函数必须返回 { status_code, body }HTTP worker 才能构建响应。 // 直接返回负载会导致 HTTP body 为空。 type httpResponse struct { StatusCode int json:status_code Body any json:body }这是理解整个示例的关键凡是暴露在 HTTP 上的函数必须使用引擎的 HTTP 信封协议。请求参数位于body字段之下而非顶层响应必须形如{ status_code: ..., body: ... }否则引擎的 HTTP worker 无法把调用结果组装成 HTTP 响应。3.2 注册 worker 与函数url : os.Getenv(III_URL) if url { url iii.DefaultEngineURL } client : iii.RegisterWorker(url)RegisterWorker是 Go SDK 的惯用入口对应 Node SDK 的registerWorker与 Rust SDK 的register_worker创建 client 并在后台开始连接立即返回。之后注册的函数和触发器会在Connect报告首次连接成功时统一发送给引擎。这解释了为什么下面可以先注册、后连接。III_URL环境变量用于覆盖引擎地址未设置时回退到DefaultEngineURL。接下来注册函数if err : client.RegisterFunction(hello::greet, func(ctx context.Context, data json.RawMessage) (any, error) { var req httpRequest if err : json.Unmarshal(data, req); err ! nil { return nil, err } name : req.Body.Name if name { name world } return httpResponse{ StatusCode: 200, Body: map[string]string{message: Hello, name !}, }, nil }); err ! nil { log.Fatalf(register function: %v, err) }Handler 签名是func(ctx context.Context, data json.RawMessage) (any, error)定义见 client.go接收原始 JSON 负载返回值会被 SDK 序列化为调用结果返回 error 则产生调用错误。因为该函数通过 HTTP 暴露所以它读取req.Body中的请求并返回{ status_code, body }信封。3.3 绑定 HTTP 触发器if err : client.RegisterTrigger( hello-http, // 触发器实例 ID http, // 触发器类型 hello::greet, // 目标函数 json.RawMessage({api_path:/greet,http_method:POST}), nil, // 可选 metadata ); err ! nil { log.Fatalf(register trigger: %v, err) }RegisterTrigger的完整签名见 client.go是func (c *Client) RegisterTrigger(id, triggerType, functionID string, config json.RawMessage, metadata ...json.RawMessage) error这里把hello::greet函数暴露为引擎 HTTP 端口上的POST /greet。config中的api_path与http_method就是 HTTP 触发器的路由配置metadata最多传一个作为触发器的附加 JSON 元数据实现细节见registerTrigger中对metadata长度的校验client.go。3.4 连接与 socket 往返演示connectCtx, cancel : context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err : client.Connect(connectCtx); err ! nil { log.Fatalf(connect to engine at %s: %v, url, err) } defer client.Close() log.Printf(worker connected to %s, url)Connect会阻塞直到首次连接建立或 ctx 取消、重连预算耗尽见 client.go。随后示例通过 socket 直接调用一次函数验证往返callCtx, cancel : context.WithTimeout(context.Background(), 5*time.Second) defer cancel() result, err : client.Trigger(callCtx, iii.TriggerRequest{ FunctionID: hello::greet, Data: json.RawMessage({body:{name:world}}), }) if err ! nil { log.Printf(trigger hello::greet: %v, err) } else { log.Printf(hello::greet returned: %s, result) }注意这里的 payload 刻意复用了 HTTP 信封{body:{name:world}}与引擎对POST /greet发送的内容形态一致因此 socket 调用与 HTTP 调用会命中相同的处理逻辑。3.5 持续服务直到被中断log.Print(serving; POST to /greet on the engines HTTP port, or Ctrl-C to exit) sig : make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) -sig log.Print(shutting down)worker 通过信号通道阻塞主协程保持进程存活使 HTTP 触发器保持可调用状态。收到SIGINT/SIGTERM后优雅退出defer client.Close()会停止重连、取消挂起调用并关闭 socket。四、为什么 Content-Type 会决定成败README 特别强调Content-Type 头至关重要——引擎只把 JSON body 解析进请求信封而 curl 的-d默认发送application/x-www-form-urlencoded。这并非 curl 的边角知识而是引擎 HTTP worker 的解析行为决定的携带Content-Type: application/json时请求体{name:world}被解析进信封的body字段handler 读到req.Body.Name world返回{message:Hello, world!}缺省时form-urlencoded请求到达时body为nulljson.Unmarshal后Name为空handler 回退到默认值world。也就是说同样的 URL 和参数Content-Type 不同结果可能是 Hello, world!来自默认回退而非你传入的名字。main.go 的包注释中明确记录了这一点。这也解释了 SDK 测试为何总是显式设置 JSON 头见下文第五节。五、源码级验证全链路集成测试如何印证示例示例所演示的路径并非孤例SDK 仓库中sdk/packages/go/iii/tests/api_triggers_test.go//go:build integration提供了一个几乎同构的集成测试TestHTTPTriggerRoundtrip覆盖完整入站路径engine → InvokeFunction → handler → InvocationResult → HTTP response注册函数test::http::go::greethandler 读取Body.Name并返回{status_code: 200, body: {greeting: hi name}}api_triggers_test.go以唯一化路径注册 HTTP 触发器api_path加 nonce 后缀避免与引擎全局 HTTP 路由表冲突api_triggers_test.go显式设置Content-Type: application/json后POST引擎 HTTP API断言 200 状态码与响应体api_triggers_test.go。测试注释明确指出它exercises the HTTP enveloperequest under bodyresponse as {status_code, body}与iii-example示例的手写信封类型完全对应——两者相互印证了 HTTP 触发器协议的契约。此外单元测试Example_helloWorkerexample_test.go在go doc层面复现了同样的注册流程RegisterWorker(DefaultEngineURL)→RegisterFunction→RegisterTrigger→Connect→Close可作为不依赖真实引擎的对照阅读材料。六、从示例走向生产理解 SDK 的支撑能力示例只用到了 SDK 的最小 API但它背后是一套完整的连接与调用模型。理解这些示例就能轻松扩展到真实业务连接行为constants.go 与 client.go指数退避重连起始 1s、每次 ×2、封顶 30s、±30% 抖动、默认无限重试与 Node SDK 完全一致可用WithReconnectConfig覆盖离线缓冲断连期间发出的调用帧被缓冲重连后在注册重放之后冲刷所有注册信息从内存注册表重放worker 元数据runtime: go在每次连接的最后注册见 client.go。调用语义TriggerRequestclient.go默认 await等待调用结果默认超时 30sDefaultInvocationTimeoutVoidAction()fire-and-forget立即返回EnqueueAction(queue)经命名队列路由并等待回执错误是类型化的errors.Is(err, iii.ErrTimeout)判断超时、errors.As(err, ie)提取携带远端Code/Message/Stacktrace的*iii.InvocationError。注册扩展client.goRegisterFunctionOptions.Metadata可附加任意注册元数据handler 的 ctx 上可携带每次调用的元数据 sidecar通过iii.MetadataFromContext(ctx)读取ok为false表示调用方未附加——handler 签名因此永不变化。示例之外SDK 还提供RegisterFunctionTyped反射推断请求/响应 JSON Schema 并广播为request_format/response_format、自定义触发器类型RegisterTriggerType对应TriggerHandler接口见 triggers.go以及流式数据通道CreateChannel可在 Go SDK README 中查阅完整 API 表。七、小结sdk/packages/go/iii-example用最少的代码展示了 iii Go SDK 的完整工作流一个进程打开单条 WebSocket 连接成为 worker注册函数与触发器引擎在 socket 上调用函数、worker 回包。三步运行法启动引擎 →go run .→ curl 带 JSON 头调用即可验证 Hello WorldContent-Type的坑、HTTP 信封的{body}/{status_code, body}约定、III_URL覆盖引擎地址是示例中三个最值得记住的实战细节。对协议与实现细节感兴趣的读者可继续阅读 client.go 的并发模型注释单一 writer 协程 读循环 supervisor 生命周期与 api_triggers_test.go 的集成测试从源码层面验证本文所述的全部行为。【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表