Skip to content

feat(router): add static router configuration injection#3252

Open
Aetherance wants to merge 14 commits intoapache:developfrom
Aetherance:feat/router/api-v3
Open

feat(router): add static router configuration injection#3252
Aetherance wants to merge 14 commits intoapache:developfrom
Aetherance:feat/router/api-v3

Conversation

@Aetherance
Copy link
Copy Markdown
Contributor

@Aetherance Aetherance commented Mar 14, 2026

Description

Currently, router configurations in dubbo-go can only be updated through dynamic configuration centers. This PR introduces support for statically configuring routers through code, eliminating the need for a config center.

Fixes #3219


Key Changes

1. StaticConfigSetter Interface

A new interface StaticConfigSetter is introduced in
cluster/router/router.go.

Routers implementing this interface can receive static router
configurations and decide whether to apply them based on their own
logic.

type StaticConfigSetter interface {
    SetStaticConfig(cfg *global.RouterConfig)
}

2. Client Options Integration

Router configurations can now be provided through client initialization.

New APIs:

  • WithRouter(...)
  • SetClientRouters(...)

Configuration propagation:

ClientOptions.Routers
        ↓
ReferenceOptions.Routers
        ↓
URL attribute (RoutersConfigKey)

3. Router Chain Injection

Static router configurations are injected into the router chain during
initialization.

New functions added in chain.go:

  • injectStaticRouters()
  • injectRouterConfig()

Features supported:

  • Condition matching
  • Application name matching
  • force flag
  • enabled flag

4. Router Implementations

Condition Router
  • Parses condition rules
  • Supports service-level and application-level scopes
  • Handles force and enabled flags
  • Applies routing rules during invocation
Tag Router
  • Validates tag configurations
  • Ensures correct enabled / valid flag handling
  • Integrates with existing tag-based routing logic

Usage

Static router rules can now be defined directly in application code:

ins, _ := dubbo.NewInstance(
    dubbo.WithName("demo-consumer"),
    dubbo.WithRouter(
        router.WithScope(constant.RouterScopeService),
        router.WithKey("com.example.HelloService"),
        router.WithConditions([]string{
            "tag = gray => host = gray-provider",
        }),
    ),
)

Router v3 API Configuration Flow

The following diagram shows how router configurations propagate from
application code to the router chain at runtime.

Application Code

  dubbo.WithRouter(router.Option...)  (InstanceOption)
          │
          ▼
  dubbo.InstanceOptions.Router []*global.RouterConfig
          │
          ▼
  client.SetClientRouters(routers)  (ClientOption)
          │
          ▼
  client.ClientOptions.Routers []*global.RouterConfig
          │
          ▼
  setRouters(routers)  (ReferenceOption)
          │
          ▼
  client.ReferenceOptions.Routers []*global.RouterConfig
          │
          ▼
  URL.SetAttribute(RoutersConfigKey, routers)


RouterChain Initialization

  injectStaticRouters(url)
          │
          ├── URL.GetAttribute(RoutersConfigKey)
          │
          └── router.SetStaticConfig(cfg)
                  │
                  └── routerConfigs.Store(key, cfgCopy)


Routing Phase

  Route(invokers, url, invocation)
          │
          └── routerConfigs.Load(key)

Checklist

  • Target branch is develop
  • Local tests passed
  • Tests added to verify the feature or fix

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 85.10638% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.66%. Comparing base (60d1c2a) to head (2549c7d).
⚠️ Report is 767 commits behind head on develop.

Files with missing lines Patch % Lines
cluster/router/chain/chain.go 81.48% 3 Missing and 2 partials ⚠️
cluster/router/condition/dynamic_router.go 84.37% 3 Missing and 2 partials ⚠️
client/action.go 0.00% 1 Missing ⚠️
client/client.go 0.00% 1 Missing ⚠️
cluster/router/tag/match.go 0.00% 0 Missing and 1 partial ⚠️
cluster/router/tag/router.go 93.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3252      +/-   ##
===========================================
+ Coverage    46.76%   49.66%   +2.89%     
===========================================
  Files          295      469     +174     
  Lines        17172    34660   +17488     
===========================================
+ Hits          8031    17213    +9182     
- Misses        8287    16065    +7778     
- Partials       854     1382     +528     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Alanxtl Alanxtl linked an issue Mar 15, 2026 that may be closed by this pull request
2 tasks
@Alanxtl Alanxtl added ✏️ Feature 3.3.2 version 3.3.2 labels Mar 15, 2026
Copy link
Copy Markdown
Contributor

@Alanxtl Alanxtl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 测试覆盖率太低了
  2. 在dubbo-website里面写一下文档
  3. 在dubbo-go-samples里面写一下sample

Copy link
Copy Markdown
Contributor

@AlexStocks AlexStocks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

加了 unit test,但之前 Alanxtl 提的几个设计问题还没回应,详见行内评论。

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for injecting router configurations statically (via code/client options) into the Dubbo-Go router chain, so routing rules can take effect without relying on a dynamic config center.

Changes:

  • Plumbs []*global.RouterConfig from instance/client/reference options into the consumer URL attributes via constant.RoutersConfigKey.
  • Injects matching static router configs into the RouterChain during NewRouterChain() initialization via a new router.StaticConfigSetter interface.
  • Adds static-config application logic + tests for condition router and tag router.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
dubbo.go Passes instance-level cloned router configs into client options (SetClientRouters).
common/constant/key.go Adds RoutersConfigKey and scope constants (service / application).
cluster/router/router.go Introduces StaticConfigSetter interface for routers that accept static injection.
cluster/router/chain/chain.go Implements static router config matching + injection into routers during chain init.
cluster/router/chain/chain_test.go Adds unit tests for scope/key matching and injection filtering.
cluster/router/condition/dynamic_router.go Adds SetStaticConfig for condition routing + scoped wrappers.
cluster/router/condition/router_test.go Adds tests for static condition config behavior and scope gating.
cluster/router/tag/router.go Adds SetStaticConfig for tag router storing cloned configs.
cluster/router/tag/router_test.go Adds tests for static tag config cloning/storage + parse validation.
client/options.go Adds router config fields/options for client and reference (Routers, WithRouter, WithClientRouter, SetClientRouters).
client/options_test.go Adds tests for new reference/client router options behavior.
client/client.go Propagates client router configs into reference initialization (setRouters).
client/action.go Stores reference router configs into URL attributes (RoutersConfigKey).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@AlexStocks

This comment was marked as duplicate.

Copy link
Copy Markdown
Contributor

@AlexStocks AlexStocks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 PR 还缺少配套文档和示例代码:

  1. 需要在 dubbo-website 添加使用文档,说明如何使用 WithRouter() 配置静态路由,以及静态配置与动态配置的关系(这个看你时间吧)
  2. 需要在 dubbo-go-samples 添加可运行的示例

@Aetherance
Copy link
Copy Markdown
Contributor Author

Aetherance commented Mar 25, 2026

apache/dubbo-go-samples#1059
apache/dubbo-website#3202

已在 samples 提交示例和在 website 提交文档
@AlexStocks @Alanxtl

Copy link
Copy Markdown
Contributor

@Alanxtl Alanxtl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

// Process is designed for dynamic config-center updates that arrive as YAML text.
// Static and dynamic rules are not merged: later Process updates replace the current state built here.
func (p *PriorityRouter) SetStaticConfig(cfg *global.RouterConfig) {
if cfg == nil || len(cfg.Tags) == 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 这里缺少 scope 约束。tag router 的运行时查找 key 一直是“provider application + tag suffix”,所以只有 application-scope 规则能命中;如果用户静态注入了 scope: service 且带 tags,这段代码仍会把规则存进去,但后续永远不会被 Route() 取到,表现成“配置通过但不生效”。建议和 condition router 一样在入口处显式限制 cfg.Scope == constant.RouterScopeApplication,或者在 RouterConfig.Init() 阶段直接拒绝 service-scope tag 规则。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修复

return routerCfg.Key == url.SubURL.ServiceKey()
}
return routerCfg.Key == url.ServiceKey()
case constant.RouterScopeApplication:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 这里按 consumer URL 的 application 过滤 application-scope 规则,但 tag router 运行时取 key 的地方是 invokers[0].GetURL().GetParam(application),也就是 provider application。两边语义不一致:规则能在初始化时注入进去,真正路由时却会用另一套 key 去查,application-scope 的静态 tag 规则只有在 consumer/provider application 恰好同名时才会生效。建议把 application-scope 静态规则的匹配和存储 key 统一到同一侧,否则这个特性本身就是不稳定的。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不应该再加一层错误使用了 consumer url 的 isRouterMatch(下层已实现按 scope 分配 router config 相关逻辑) 已删除该错误逻辑

而对于 tag router 来说 router config 查找和 存储 key 的语义是一致的,都是 provider application,存储时使用的 provider application 是由router config 中的 key 字段提供的

Copy link
Copy Markdown
Contributor

@AlexStocks AlexStocks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] client/options.go:862-864 nil 检查冗余,append 对 nil slice 安全,直接 append 即可。

[P2] client/options.go:448 注释不准确,应明确说明这是用户 API,与 setRouters 的区别。

[P0] cluster/router/chain/chain.go:178 并发安全问题:injectStaticRouters 持有读锁时调用 injectRouterConfig,后者又获取读锁,设计不清晰。建议 injectStaticRouters 不加锁,由 injectRouterConfig 统一加锁。

[P1] cluster/router/chain/chain.go:151 类型断言失败只记录警告,可能导致配置未生效而用户不知情,建议返回 error 或记录 error 级别日志。


// WithClientRouter appends router configurations to the client options.
// This is a user-facing option for incrementally adding routers.
// It appends to the current router slice instead of replacing it.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] nil 检查冗余。append 对 nil slice 安全,直接 opts.Routers = append(opts.Routers, routers...) 即可。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已去除 nil 冗余检查

c.injectRouterConfig(routerCfg)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] 并发安全问题。injectStaticRouters 持有读锁时调用 injectRouterConfig,后者又获取读锁。虽然 RLock 可重入,但设计不清晰。建议 injectStaticRouters 不加锁,由 injectRouterConfig 统一加锁。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

injectStaticRouters 并没有加锁啊

if !ok {
return
}
staticRoutersAttr, ok := staticRoutersAttrAny.([]*global.RouterConfig)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 类型断言失败只记录警告,可能导致配置未生效而用户不知情。建议返回 error 或记录 error 级别日志。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里已改为 Error 级日志

@sonarqubecloud
Copy link
Copy Markdown

@Aetherance
Copy link
Copy Markdown
Contributor Author

Hi @AlexStocks, any further comments or concerns on this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Support Configuring Router via v3 API

5 participants