XnLemon opened a new issue, #3686:
URL: https://github.com/apache/dubbo-go/issues/3686

   Sub-issue of #3672
   ### 背景
   
   dubbo-go 与 dubbo-go-extensions 之间一直没有统一的扩展配置协议(对应主 issue #3672)。当前问题:
   
   - 用户要直接依赖 `hystrix-go` 并调用 `ConfigureCommand`;
   - 用户必须知道 `hystrix_consumer` / `hystrix_provider` 这类扩展内部 filter 名字;
   - consumer 用 `client.WithFilter`、provider 用 `server.WithFilter`,入口不统一;
   - 扩展配置无法以扩展自己的类型被 `dubbo.Load()` 加载;
   - 扩展配置与 filter 启用之间没有统一生命周期。
   
   本 issue 是第一步:**只建立核心侧的抽象基础类型(契约层),不接入 loader、不实现生命周期运行时、不做 filter 
绑定**。核心约束是:这一层**不能出现任何具体扩展名**(如 `WithHystrix`)。
   
   ### 本 issue 建立的基础类型
   
   统一放在 `common/extension` 包。
   
   #### 1. `Scope` —— 扩展作用域(位掩码)
   
   ```go
   type Scope uint8
   
   const (
       InstanceScope Scope = 1 << iota // 1,dubbo.Instance 生命周期
       ClientScope                      // 2,client/consumer 生命周期
       ServerScope                      // 4,server/provider 生命周期
   )
   ```
   
   - **为什么是位掩码**:`Definition.Scopes` 要表达「一个扩展支持哪些作用域」(如 Hystrix 同时支持 
`ClientScope | ServerScope`),而 `Context.Scope` 永远是单一具体值。位掩码能同时满足这两种语义。
   - **为什么保留 InstanceScope**:Hystrix 本身不提供 Instance 级能力,但 tracing / metrics 
这类**角色无关**的扩展需要在 Instance 初始化阶段配置。InstanceScope 
是并列作用域,不能因为首个落地扩展(Hystrix)用不到就删掉。
   
   #### 2. `RoleNone` —— 无角色常量
   
   ```go
   const RoleNone common.RoleType = -1
   ```
   
   - 复用已有的 `common.RoleType`(`CONSUMER` / `PROVIDER`),不新造枚举。
   - Instance 没有 consumer/provider 角色,用 `-1` 表示,**刻意落在 `common.RoleType` 的 URL 
角色常量之外**,避免与真实角色混淆。
   
   #### 3. `Resource` —— 资源标识(核心构造、扩展只读)
   
   ```go
   type Resource struct {
       ServiceKey string
       Interface  string
       Method     string   // 空表示服务级资源
       Group      string
       Version    string
   }
   
   func (r Resource) Validate() error // 校验 ServiceKey == 
common.ServiceKey(Interface, Group, Version)
   ```
   
   - 扩展的 filter 可能要**按服务甚至方法**做差异化(Hystrix 的 command 
就是按服务维度配置的),所以需要把规范化的资源身份传给扩展。
   - `Validate` 保证核心下发的 `ServiceKey` 与 `Interface/Group/Version` 
一致,杜绝扩展拿到自相矛盾的资源。
   
   #### 4. `Context` —— 生命周期上下文
   
   ```go
   type Context struct {
       Scope    Scope
       Role     common.RoleType
       Config   any           // 扩展自己的 typed 配置
       Resource *Resource     // nil 表示尚未绑定具体资源
   }
   
   func (c Context) Validate() error
   ```
   
   - 把扩展回调(`Init` / `Filters` / `Close`)需要的全部信息打包成**一个对象**传入,避免回调签名不断膨胀。
   - `Config` 是 `any`:核心只负责构造和传递,**不理解其结构**(类型擦除的关键点)。
   - `Resource` 可空:`Build`(生命周期初始化)阶段还没绑定资源,`BindResource` 阶段才填充。
   - `Validate` 固定 scope/role 
合法组合:`InstanceScope→RoleNone`、`ClientScope→CONSUMER`、`ServerScope→PROVIDER`。校验逻辑与具体扩展无关。
   
   #### 5. `RawNode` / `RawConfig` —— parser 无关的配置树
   
   ```go
   type RawNode interface {
       Child(key string) (RawNode, bool) // 字面 key 精确匹配
       Value() any
       Present() bool
   }
   
   type RawConfig struct {
       Full     RawNode // 完整扩展子树
       Selected RawNode // 按 scope 选中的 consumer/provider 分支
   }
   ```
   
   - **为什么存在**:核心编译期不知道扩展配置的字段结构,无法用静态 struct 反序列化,只能把 
`dubbo.extensions.<prefix>` 子树**原样抠出**,以无类型树形式交给扩展自己 `Decode`。
   - **为什么 `Child` 是字面 key 而非路径**:koanf 的 `.` delimiter 会把 
`greet.GreetService:::Greet` 里的 `.`/`:` 当成路径分隔符拆掉。`Child` 的精确匹配绕开 delimiter 
语义,保证扩展 key 不被拆分。
   
   #### 6. `Option` —— typed 配置选项
   
   ```go
   type Option interface {
       Prefix() string          // 归属哪个扩展
       Apply(config any) error  // 应用到扩展自己的配置
   }
   ```
   
   - 核心只按 `Prefix` 分组、按声明顺序 `Apply`,**不关心 option 内部语义**。
   - 具体 option 类型由扩展包自己定义(对应 issue 里的 `hystrix.WithConfig(...)` / 
`hystrix.WithTimeout(...)`)。
   
   #### 7. `FilterSpec` —— filter 贡献描述
   
   ```go
   type FilterSpec struct {
       ID      string              // 扩展内部身份,用于去重/诊断,非用户可见名
       Factory func() filter.Filter // 工厂而非实例:按资源/上下文创建
       Order   int                 // 确定性排序
   }
   ```
   
   - 统一框架 filter 与扩展 filter 的**载体**,是后续「框架/扩展 filter 合并」的基础。
   - 用工厂函数而非直接实例,因为 filter 可能要按资源上下文带状态创建。
   
   #### 8. `Definition` —— 扩展定义(核心与扩展交互的唯一抽象)
   
   ```go
   type Definition struct {
       Prefix    string                            // 注册名,也是 YAML 的 
dubbo.extensions.<prefix>
       Scopes    Scope                             // 声明支持的作用域
       NewConfig func() any                        // 创建配置实例
       Decode    func(RawConfig, any) error        // 解码 raw 配置
       Init      func(*Context) error              // 初始化
       Filters   func(*Context) ([]FilterSpec, error) // 声明该资源的 filter
       Close     func(*Context) error              // 释放
   }
   ```
   
   - 这是整个契约的**核心**:核心只通过这个抽象与扩展交互,不 import 具体扩展包。
   - 每个字段对应生命周期一个阶段:`NewConfig → Decode → Init → Filters → Close`(配置优先级:默认值 < 
raw YAML < typed option,在后续 PR 落地)。
   - `validate()` 强制:Prefix 非空、Scopes 合法、NewConfig 非空。
   
   #### 9. 注册表
   
   ```go
   var definitions = NewRegistry[Definition]("extension definition")
   
   func Register(def Definition) error   // 拒绝重复 Prefix
   func MustRegister(def Definition)     // 供扩展 init() 使用,失败 panic
   func Lookup(prefix string) (Definition, bool)
   func Unregister(prefix string)        // 测试用
   ```
   
   - 复用现有泛型 `Registry[T]`。
   - 注册后 Definition 视为不可变,避免运行时行为漂移。
   
   ### 为什么这么设计
   
   1. **核心零扩展依赖**:所有类型里没有任何 `Hystrix` / `Sentinel` 字样,扩展通过 side-effect import + 
`MustRegister` 接入,核心用抽象 `Definition` 反查。
   2. **类型擦除 + 延迟解码**:核心只搬运 `RawNode`,解码责任下放给 `Decode`,这是「核心不知道扩展结构」的唯一可行解。
   3. **字面 key 语义**:`RawNode.Child` 精确匹配,从根上解决 `.`/`:` 被误拆的问题。
   4. **作用域与角色显式化**:`Scope` + `Role` 的组合由 `Validate` 固化,扩展在错误作用域下使用会明确报错,而非静默失效。
   5. **工厂而非实例**:`FilterSpec.Factory` 让 filter 可以按资源上下文创建,为后续资源级差异化铺路。
   
   ### 后续如何扩展
   
   - **角色无关扩展**:tracing / metrics 等用 `InstanceScope` 接入,无需改协议。
   - **非 filter 型扩展**:当前契约只覆盖 filter 型扩展;若未来要统一自定义 registry、config 
post-processor 等,可在此基础上扩展 `Definition` 的能力字段。
   
   ### 范围与验收标准
   
   - [ ] 在 `common/extension` 定义上述基础类型,不含任何具体扩展名
   - [ ] `Definition.validate()` / `Context.Validate()` / `Resource.Validate()` 
覆盖非法输入
   - [ ] 注册表拒绝重复 Prefix,`MustRegister` 失败 panic
   - [ ] 单元测试覆盖契约校验与注册行为
   - [ ] 核心包可编译,且不依赖任何具体扩展


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to