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 |
payload | Any,其 value 直接就是 JSON 字节 |
metadata | tenant_id / user_id / trace_id |
request_id | 调用方给的幂等/追踪标识,原样回填到响应里 |
payload.type_url 是 type.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.Call,operation 为
flare.capability.v1.health_check,返回 ok: true 即健康。
需要特殊探活语义时,可在注册的 labels 里声明协议名 —— 但对应的探针实现
必须已部署在平台侧,否则平台会把你判为「声明的协议没有探针」而不是静默
降级成通用检查。静默降级等于把装配缺失伪装成健康。
§8. 注册契约
见 自己开发一个插件。字段全集与语义在
flare-plugin-host 的 API 文档里。