apache / apache/dubbo-go

[Feature] Unify extension options and YAML configuration with Hystrix

Closed
#3,672 6 comments 0 reactions 1 assignee Claimed by @XnLemon View on GitHub
✏️ Feature 3.3.3
Dominant language
Go
Stars
5k
Forks
1k
Avg merge
3d 6h
Merged PRs (30d)
29

Description

## 任务范围

本 issue 以 Hystrix 作为第一个完整落地案例,用来验证 dubbo-go 与 [dubbo-go-extensions](https://github.com/apache/dubbo-go-extensions) 的统一扩展配置/option 协议。

Hystrix 只是示例,不代表本任务只服务于 Hystrix,也不要求本次一次性迁移所有 extension。核心接口应保持通用,不能在 dubbo-go 中加入 `WithHystrix` 等具体扩展名称。

## 当前现状

展开查看当前 Hystrix 使用方式

### Consumer

```go
import (
"context"
"github.com/afex/hystrix-go/hystrix"
_ "github.com/apache/dubbo-go-extensions/filter/hystrix"
"dubbo.apache.org/dubbo-go/v3"
"dubbo.apache.org/dubbo-go/v3/client"
"dubbo.apache.org/dubbo-go/v3/registry"
)

func init() {
cmdName := "dubbo:consumer:greet.GreetService:::Greet"
hystrix.ConfigureCommand(cmdName, hystrix.CommandConfig{
Timeout: 1000,
MaxConcurrentRequests: 10,
RequestVolumeThreshold: 5,
SleepWindow: 5000,
ErrorPercentThreshold: 50,
})
}

func main() {
ins, _ := dubbo.NewInstance(
dubbo.WithRegistry(
registry.WithZookeeper(),
registry.WithAddress("127.0.0.1:2181"),
),
)
cli, _ := ins.NewClient()
svc, _ := greet.NewGreetService(cli, client.WithFilter("hystrix_consumer"))

_, _ = svc.Greet(context.Background(), &greet.GreetRequest{Name: "test"})
}
```

### Provider

```go
import (
_ "github.com/apache/dubbo-go-extensions/filter/hystrix"
"dubbo.apache.org/dubbo-go/v3/server"
)

func main() {
srv, _ := server.NewServer(
server.WithFilter("hystrix_provider"),
)
// ... rest of server setup
}
```

### 当前存在的问题

- 用户需要直接依赖 `hystrix-go` 并调用 `ConfigureCommand`;
- 用户需要知道 `hystrix_consumer` / `hystrix_provider` 这两个扩展内部 filter 名称;
- consumer 使用 reference-level `client.WithFilter`,provider 使用 server/service-level filter 配置,入口不统一;
- dubbo-go 与 dubbo-go-extensions 没有统一的 typed option 协议;
- 使用 `dubbo.Load()` 加载 YAML 时,无法以扩展自己的类型接收 Hystrix 配置;
- 扩展配置和扩展 filter 启用之间没有统一生命周期。

## 目标

建立 dubbo-go 与 dubbo-go-extensions 之间统一的扩展配置/option 协议:

1. 扩展可以提供自己的 `WithXXX` typed options;
2. `client.NewClient` 和 `server.NewServer` 使用同一个扩展 option 协议;
3. consumer/provider 角色由 client/server 创建上下文决定,不要求用户调用 `WithConsumer` / `WithProvider`;
4. 核心协议保留 Instance scope,支持未来角色无关的 Instance 级 extension;
5. 用户不再手工传入扩展内部 filter 名称;
6. `dubbo.Load()` 可以加载扩展 YAML 配置;
7. 允许对现有 Hystrix 配置和 filter 使用方式进行破坏性更新;
8. 核心接口不依赖任何具体 extension。

Hystrix 本身不提供 Instance 级 filter API。一个 Instance 既可能创建 client,也可能创建 server,Hystrix 的 filter 绑定必须由 `client.NewClient` / `server.NewServer` 的上下文决定。

但是,统一扩展协议必须保留未来的 Instance scope。tracing、metrics 或其他角色无关的 extension 可以通过 `dubbo.WithExtension(...)` 在 Instance 初始化阶段配置。Instance scope 与 client/server scope 是并列的作用域,不应因为 Hystrix 当前不需要 Instance API 而从通用协议中删除。
## 目标 API

### Consumer

```go
import (
"dubbo.apache.org/dubbo-go/v3/client"
hystrix "github.com/apache/dubbo-go-extensions/filter/hystrix"
)

cli, err := client.NewClient(
client.WithExtension(
hystrix.WithConfig(
hystrix.WithCommandName("greet.GreetService:::Greet"),
hystrix.WithTimeout(1000),
hystrix.WithMaxConcurrentRequests(10),
hystrix.WithRequestVolumeThreshold(5),
hystrix.WithSleepWindow(5000),
hystrix.WithErrorPercentThreshold(50),
),
),
)
```

`client.WithExtension` 负责提供 consumer 上下文,Hystrix 根据该上下文选择 consumer 配置并自动绑定 `hystrix_consumer`。

用户不再需要:

```go
client.WithFilter("hystrix_consumer")
```

### Provider

```go
import (
"dubbo.apache.org/dubbo-go/v3/server"
hystrix "github.com/apache/dubbo-go-extensions/filter/hystrix"
)

srv, err := server.NewServer(
server.WithExtension(
hystrix.WithConfig(
hystrix.WithCommandName("greet.GreetService:::Greet"),
hystrix.WithTimeout(1000),
),
),
)
```

`server.WithExtension` 负责提供 provider 上下文,Hystrix 根据该上下文选择 provider 配置并自动绑定 `hystrix_provider`。

用户不再需要:

```go
server.WithFilter("hystrix_provider")
```

### API 语义

```text
client.WithExtension(...) -> dubbo-go 的 consumer 扩展入口
server.WithExtension(...) -> dubbo-go 的 provider 扩展入口
hystrix.WithConfig(...) -> 绑定 Hystrix 扩展配置
hystrix.WithTimeout(...) -> 配置 Hystrix 参数
```

不建议在 dubbo-go 核心中提供 `extension.WithHystrix`。具体扩展的绑定方法应由具体扩展包提供,避免用户误以为 Hystrix 属于核心 `common/extension` 包。

## YAML 设计

YAML 中必须显式表达 consumer/provider,不能从 command resource name 推断角色。

不增加 `commands` 包装层,每个 command 直接作为对应角色下的配置 key:

```yaml
dubbo:
extensions:
hystrix:
consumer:
"greet.GreetService:::Greet":
timeout: 1000
max-concurrent-requests: 10
request-volume-threshold: 5
sleep-window: 5000
error-percent-threshold: 50

"com.example.UserService:::GetUser":
timeout: 2000
max-concurrent-requests: 20

provider:
"greet.GreetService:::Greet":
timeout: 1500
max-concurrent-requests: 10
```

其中:

- `consumer` / `provider` 是核心约定的角色层级;
- command key 是逻辑 resource name,角色已经由外层的 `consumer` / `provider` 配置块表达;
- 新设计不再要求或生成 `dubbo:consumer:` / `dubbo:provider:` 这类对外 command name 前缀;
- `commands` 不作为额外包装层;
- YAML 解析必须保留 command key 的完整字符串,不能让 `.` 或 `:` 被核心配置路径解析器拆分。

使用 YAML 时,用户只需要导入扩展以完成注册:

```go
import _ "github.com/apache/dubbo-go-extensions/filter/hystrix"

func main() {
if err := dubbo.Load(); err != nil {
panic(err)
}
}
```

配置加载完成后:

- client 上下文读取 `dubbo.extensions.hystrix.consumer`;
- server 上下文读取 `dubbo.extensions.hystrix.provider`;
- client 自动绑定 `hystrix_consumer`;
- server 自动绑定 `hystrix_provider`;
- 用户无需在 YAML 中配置这两个内部 filter 名称。

### Prefix 语义

角色已经通过 YAML 的 `consumer` / `provider` 层级以及 `client` / `server` 上下文明确表达,因此不设计通用的 role prefix 机制。

新的 Hystrix 配置和 option 不再要求用户使用或理解 `dubbo:consumer:` / `dubbo:provider:` 命令名前缀。它们属于旧实现的一部分,可以随着本次破坏性更新移除。

`hystrix` 仍然作为扩展配置注册名,用于定位 `dubbo.extensions.hystrix`,但它不是 role prefix,也不负责生成 command name。

---
## 初步接口设计

### dubbo-go 核心

在 `common/extension` 中定义通用扩展协议,但不包含任何具体扩展名称:

```go
type Scope uint8

const (
InstanceScope Scope = 1 << iota
ClientScope
ServerScope
)

type Option interface {
Prefix() string
Apply(*Context) error
}

// context 这个东西我感觉设计也不太好 具体实现的时候需要考虑一下 instance层级的extension 以及Consumer/Provider这个层级的extension该怎么区分
// 因为对于不同的层级传入的api的地方不一样 dubbo.WithExtension和client.WithExtension 看有没有统一一点的方法
type Context struct {
Scope Scope
Role common.RoleType
Config any
}

type Definition struct {
Prefix string
Scopes Scope
New func() any
Init func(any) error

ConsumerFilters func(any) []string
ProviderFilters func(any) []string
}
```

核心提供:

```go
func dubbo.WithExtension(opts ...extension.Option) InstanceOption
func client.WithExtension(opts ...extension.Option) ClientOption
func server.WithExtension(opts ...extension.Option) ServerOption
```

核心职责:

- 保存并应用扩展 option;
- 根据 Instance/client/server scope 校验扩展 option 是否适用;
- 加载 `dubbo.extensions`;
- 为扩展创建独立配置实例;
- 向扩展传递 client/server role context;
- 将扩展 filter 合并到最终 filter chain;
- 对重复 filter 做去重;
- 保留用户显式 filter 配置的覆盖能力;
- 不包含 Hystrix、Sentinel 等具体扩展名称。

### Hystrix 扩展

Hystrix 包定义自己的 typed option:

```go
type Option func(*Config)

func WithConfig(opts ...Option) extension.Option
func WithCommandName(name string) Option
func WithTimeout(timeout int) Option
func WithMaxConcurrentRequests(value int) Option
func WithRequestVolumeThreshold(value int) Option
func WithSleepWindow(value int) Option
func WithErrorPercentThreshold(value int) Option
```

Hystrix 配置对象负责:

- 保存 consumer/provider 下每个 logical resource name 对应的 command 配置;
- 根据 `Context.Role` 选择 consumer/provider 配置和对应 filter;
- 将配置应用到新的 Hystrix command 配置入口;
- 声明对应角色的 filter;
- 可以重构现有 filter 注册和配置初始化逻辑。

## Breaking changes

本任务允许对当前 Hystrix 使用方式进行破坏性更新,不要求兼容以下旧接口:

- 直接调用 `hystrix.ConfigureCommand`;
- 手工配置 `client.WithFilter("hystrix_consumer")`;
- 手工配置 `server.WithFilter("hystrix_provider")`;
- 使用 `dubbo:consumer:` / `dubbo:provider:` 作为 command name 前缀。

新的 option、YAML 结构和 command name 规则以本 issue 的设计为准。实现时需要同步更新 `dubbo-go-extensions/filter/hystrix` 的 README 和示例,并在变更说明中标注迁移方式。

## 实施拆分

### dubbo-go

- 新增通用 extension option/definition/context/scope 协议;
- 新增未来 Instance 级 extension 的 `dubbo.WithExtension` 入口;
- 新增 `client.WithExtension`;
- 新增 `server.WithExtension`;
- 在 client/server 初始化流程中接入扩展配置;
- 在 `dubbo.Load()` 中解析 `dubbo.extensions`;
- 支持 `consumer` / `provider` 角色配置块;
- 处理配置优先级:默认值 < YAML < option;
- 处理 filter 合并、去重和显式覆盖;
- 增加核心协议和 loader 测试。

### dubbo-go-extensions

- 为 Hystrix 增加配置定义;
- 增加 Hystrix typed options;
- 增加 consumer/provider YAML 配置解析;
- 根据显式的 consumer/provider 配置和 client/server 上下文自动绑定 filter;
- 更新中英文 README;
- 增加 option、YAML、filter 自动绑定测试。

## 验收标准

- 本 issue 的实现以 Hystrix 为首个验证扩展,但核心接口不包含 Hystrix 专用逻辑;
- 用户使用 client API 时不需要写 `hystrix_consumer`;
- 用户使用 server API 时不需要写 `hystrix_provider`;
- 同一套 Hystrix typed option 可以接入 client/server;
- YAML 能分别配置 consumer/provider;
- `dubbo.Load()` 可以加载至少两个 Hystrix command;
- command key 中的 `.`、`:` 不会被错误拆分;
- 核心扩展协议不依赖 role prefix,新的 Hystrix command name 不包含旧的 role prefix;
- 未来角色无关 extension 可以在不修改协议的情况下使用 Instance scope;
- Hystrix 传入 Instance scope 时能够明确返回不支持该 scope 的错误;
- 未导入 Hystrix 时,核心不依赖 Hystrix;
- dubbo-go 核心不包含 `WithHystrix` 等具体扩展名称。

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.