Skip to main content

Module plugin_contract

Module plugin_contract 

Source
Expand description

平台与插件之间的线上契约:一条 RPC 的每个字段是什么意思。

§插件契约

平台与插件之间只有一条 RPC。这份文档说清它的每个字段是什么意思,以及 哪些约定是强制的(违反了会被拒),哪些只是建议。

§1. 一条 RPC

service ExtensionPlugin {
  rpc Call(GenericRequest) returns (GenericResponse);
}

平台侧只认两个概念:能力 id(一个开放字符串)和 JSON 载荷。 它不生成也不依赖你的类型,不知道你内部是 gRPC、HTTP 还是别的什么。

这带来一个直接结论:JSON 与你原生接口之间的转换发生在你这边。 放到平台侧就意味着平台要编进你的 proto,那样每加一个插件都得改平台并发版。

§2. 请求

字段内容
operation能力 id,如 vendorx.invoice.create
payloadAny,其 value 直接就是 JSON 字节
metadatatenant_id / user_id / trace_id
request_id调用方给的幂等/追踪标识,原样回填到响应里

payload.type_urltype.googleapis.com/flare.capability.v1.PayloadJson。 这不是 protobuf 编码的 Any —— 别拿 prost 去解,直接当 JSON 字节读。

⚠️ metadata 里的上下文必须由你自己还原。 平台是进程外调用,不走 HTTP header,所以框架的 Context 中间件不会替你注入。漏掉这步的表现是所有 调用都报「找不到上下文」——看着像鉴权问题,排查方向会完全跑偏。

§3. 响应

message GenericResponse {
  bool   ok            = 1;
  Any    payload       = 2;   // 同样是 JSON 字节
  string error_code    = 3;
  string error_message = 4;
  string request_id    = 5;
}

业务失败要明确回错ok=false + error_code),别回一个空的成功。 静默成功会让调用方以为业务做了 —— 这类缺陷在链路里几乎无法定位。

§4. 能力 id 命名

建议 {域}.{子系统}.{动作},例如 vendorx.invoice.create

域用你自己的标识,别占用平台或他人的前缀。这不是技术限制(能力 id 是开放 字符串,平台不校验命名),而是多插件共存时唯一能靠的约定。

§5. 路由:声明即边界

平台的路由簿按 (租户, 插件) 作键 —— 你的进程只有一条注册记录, 承接哪些 operation 由 declared_operations 决定。

注册一次即承接全部声明。 capability_id 只是这条记录的标签, 填一个最好认的入口即可,它不限制你能接到的调用。

声明之外的调用会被平台拒绝,请求根本到不了你的进程。

如果你读到过「按 operation 最长前缀匹配」的说法:那是平台进程内 handler 的路由规则。第三方插件是进程外的,走的是上面这条精确匹配。

§6. 两类失败方向相反

类别行为例子
授权失败拒绝,不降级未安装、租户开关关闭、无用户授权
可用性失败降级,不拒绝你超时、不可达、健康检查未通过

授权是 fail-closed 的:拒绝就是拒绝。而你的实例不可用时,平台不会因此拒绝 整条链路,只会按健康状态把你排到候选末尾。

把这两类搞反是最贵的错误:授权侧一放行就是越权,可用性侧一拒绝就是 一个实例抖动拖垮整条业务链。

§7. 健康检查

默认走通用协议:平台调你的 ExtensionPlugin.Calloperationflare.capability.v1.health_check,返回 ok: true 即健康。

需要特殊探活语义时,可在注册的 labels 里声明协议名 —— 但对应的探针实现 必须已部署在平台侧,否则平台会把你判为「声明的协议没有探针」而不是静默 降级成通用检查。静默降级等于把装配缺失伪装成健康。

§8. 注册契约

自己开发一个插件。字段全集与语义在 flare-plugin-host 的 API 文档里。