Skip to content

支持规则同步到nacos#3635

Open
zackzhangCN wants to merge 5 commits into
alibaba:1.8from
zackzhangCN:detached3
Open

支持规则同步到nacos#3635
zackzhangCN wants to merge 5 commits into
alibaba:1.8from
zackzhangCN:detached3

Conversation

@zackzhangCN

Copy link
Copy Markdown

支持规则同步到nacos。
包括:流控,熔断,热点,系统,授权

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


zhangyunlong seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@bitdive-review

Copy link
Copy Markdown

BitDive Runtime Review - PR #3635

PR: #3635
Branch: detached3 -> 1.8
Method: BitDive runtime trace comparison + direct HTTP verification + targeted code inspection
Verdict: REQUEST CHANGES
Confidence: High — all five rule-query controllers verified with complete before/after trace pairs

Summary

This PR rewires all five rule-management controllers (Flow V2, Degrade, ParamFlow, Authority, System) from SentinelApiClient (direct HTTP to the Sentinel client library) to Nacos ConfigService for rule persistence. Ten new provider/publisher/config classes are added and sentinel-datasource-nacos is promoted from test to production scope. The integration pattern is reasonable; the execution has several blocking issues that runtime traces make visible.

The dominant symptom is silent data loss. The Nacos server address is hardcoded to 192.168.234.59:8848 (a private LAN IP) in application.properties. When that address is unreachable — as it is in any environment that does not host it — every rule-query endpoint still returns {success:true, code:0, msg:'success', data:[]} after a ~3 second timeout. Operators see a healthy response and an empty rule list; there is no log entry, no error indicator, no exception. Confirmed independently on all five after-traces.

Beyond the cross-cutting data-loss issue, each controller has its own behavior change worth flagging:

  • FlowControllerV2 silently swaps its rule provider via @Component('flowRuleDefaultProvider') name reuse — the old FlowRuleApiProvider is renamed to flowRuleApiProvider while the new FlowRuleNacosProvider takes the old name. Callers wiring @Qualifier('flowRuleDefaultProvider') now resolve to the Nacos bean with no log or warning.
  • DegradeController and ParamFlowRuleController drop ip/port from their query-method signatures (3 args → 1). Any client passing them positionally will break.
  • ParamFlowRuleController also removes the checkIfSupported version check, switches from CompletableFuture async to synchronous Nacos, and adds repository.saveAll([]) to the query path.
  • AuthorityRuleController removes repository.saveAll([]) from the query path — the only controller where the cache write is dropped, making in-memory behavior inconsistent across the five endpoints.
  • SystemController is the cleanest migration: ip/port validation and saveAll are both retained, and the new SystemRuleNacosProvider post-processes Nacos results to set ip/port on returned entities (verified in code; all runtime traces return data=[], so no entities were actually post-processed at runtime).

Latency on every endpoint jumps 14×–450× (e.g. AuthorityRuleController 6.7 ms → 3013 ms) because of the Nacos timeout; with a reachable Nacos server this would not apply, but it underscores that the current config ships broken.

Fix before merge:

  • sentinel-dashboard/src/main/resources/application.properties — replace the hardcoded 192.168.234.59:8848 with a configurable property (env var or external config) and fail loudly when Nacos is unreachable instead of returning empty data as success.
  • DegradeController.java / ParamFlowRuleController.java — restore ip/port parameters (or document the intentional API break and bump the controller path to /v2/...).
  • Add unit/integration coverage for the 10 new Nacos classes (currently zero test files).

Runtime Verification Matrix

Area Result Behavior Δ Meaning Evidence
FlowControllerV2 (/v2/flow/rules) Regression HTTP 200→200 · saveAll retained · provider swapped via @Component name reuse (FlowRuleApiProvider → FlowRuleNacosProvider) · 221ms→3070ms Silent bean hijack — invisible without trace before / after
DegradeController (/degrade/rules.json) Regression HTTP 200→200 · method args 3→1 (ip/port dropped) · SentinelApiClient → DegradeRuleNacosProvider · publishRules boolean→throws Exception · 9ms→3010ms API contract break on query + mutation before / after
ParamFlowRuleController (/paramFlow/rules) Regression HTTP 200→200 · method args 3→1 · checkIfSupported removed · async→sync · saveAll([]) ADDED · 12ms→3009ms API break + new write path before / after
AuthorityRuleController (/authority/rules) Regression HTTP 200→200 · validation retained · saveAll([]) REMOVED from query path · 7ms→3013ms Inconsistent caching vs other controllers before / after
SystemController (/system/rules.json) Stable* HTTP 200→200 · validation + saveAll retained · SystemRuleNacosProvider replaces SentinelApiClient · post-processing code-only · 9ms→3011ms Cleanest migration, but still silent data loss before / after
Silent data loss (all 5 after-traces) Regression success=true, code:0, msg:'success', data:[] after ~3s Nacos timeout on every rule-query endpoint Operators see healthy response with empty data; no error surfaced Each row above
Version canary (/version) Stable 200 on both sides · 1.8.11 → 1.8.10 label (head branch diverged before the version-bump commit) Spring context starts cleanly before / after
Login page (GET /) Stable HTTP 200 on both sides Login flow unchanged HTTP-only

* Stable relative to the other controllers — the same Nacos-side silent-data-loss pattern still applies.

How to read BitDive evidence

Behavior Δ in the matrix and the red/green ```diff blocks inside each Change summarize the contract change.
Evidence links open the full BitDive share: call tree · writes · downstream REST · first divergence.

  • Trace pair — full call trees compared before/after
  • HTTP only — status/body only, no JVM tree
  • Code only — inspection of source diff (no runtime trace)
  • Replay — not used in this review (no UNIT/COMPONENT replay groups exist for the new Nacos classes)

GitHub strips target="_blank"; share links open in the same tab.

Review scope and validation coverage
sentinel-dashboard
  -> FlowControllerV2.apiQueryMachineRules()         [silent bean hijack]
  -> DegradeController.apiQueryMachineRules()        [API contract break]
  -> ParamFlowRuleController.apiQueryAllRulesForMachine()  [API break + write added]
  -> AuthorityRuleController.apiQueryAllRulesForMachine()  [saveAll removed]
  -> SystemController.apiQueryMachineRules()         [cleanest migration]
     |
     +-- mutation endpoints (POST/PUT/DELETE) on all 5 controllers
     |   now call Nacos publishConfig() — same path as proven reads,
     |   code-only (not separately runtime-tested)
     |
  -> VersionController.apiGetVersion()               [canary, stable]
  -> login page (GET /)                              [stable, HTTP-only]
Behavior Scenario Evidence Status
Flow V2 query (silent bean swap) GET /v2/flow/rules?app=sentinel-dashboard Trace pair Regression confirmed
Degrade query (API break) GET /degrade/rules.json?app=...&ip=...&port=... Trace pair Regression confirmed
ParamFlow query (API break + write added) GET /paramFlow/rules?app=...&ip=...&port=... Trace pair Regression confirmed
Authority query (write removed) GET /authority/rules?app=...&ip=...&port=... Trace pair Regression confirmed
System query (cleanest migration) GET /system/rules.json?app=...&ip=...&port=... Trace pair Stable*
Silent data loss (cross-cutting) All 5 after-traces Trace pair Regression confirmed
Version canary GET /version Trace pair Stable
Login page GET / HTTP-only Stable
Mutation endpoints (POST/PUT/DELETE) All 5 controllers Code only Correctly scoped out — same Nacos publishConfig path as proven reads
Frontend sidebar/app.js flowV1 UI state removal Code only UI navigation change, out of scope

Out of scope for runtime review: CI/CD assets; frontend sidebar.html / app.js flowV1flow navigation change (UI-only; the /v1/flow/rules endpoint still exists on head); DegradeController.publishRules() signature change boolean → throws Exception (mutation path shares the same Nacos publishConfig() call proven by reads).

Change #1 - FlowControllerV2 silent bean hijack

The V2 flow controller keeps the same @Qualifier('flowRuleDefaultProvider') wiring, but the bean that name resolves to is silently swapped from FlowRuleApiProvider (direct Sentinel client HTTP) to the new FlowRuleNacosProvider. The old provider is renamed to flowRuleApiProvider; the new one takes the old name. No controller code changes — the swap is invisible without a trace.

Behavior contract

# GET /v2/flow/rules?app=sentinel-dashboard — runtime contract
- HTTP 200 OK  {success:true, data:[]}
+ HTTP 200 OK  {success:true, data:[]}   (silent — Nacos unreachable)
- bean: @Component('flowRuleDefaultProvider') = FlowRuleApiProvider
+ bean: @Component('flowRuleDefaultProvider') = FlowRuleNacosProvider
- call tree: FlowRuleApiProvider.getRules → SentinelApiClient.fetchFlowRuleOfMachine → fetchRules → fetchItems → fetchItemsAsync → executeCommand  (6 nested, 14 child calls)
+ call tree: FlowRuleNacosProvider.getRules → ConfigService.getConfig  (3s timeout, empty data, 2 child calls)
- write: InMemoryRuleRepositoryAdapter.saveAll([])
+ write: InMemoryRuleRepositoryAdapter.saveAll([])   (retained — empty list both sides)
- data source: Sentinel client library over HTTP (172.17.0.2:8719)
+ data source: Nacos ConfigService (private LAN IP 192.168.234.59:8848)
- time: 221ms
+ time: 3070ms   (14x slowdown)
# first divergence: FlowRuleNacosProvider.getRules()
- apiQueryMachineRules → FlowRuleApiProvider.getRules → SentinelApiClient chain
+ apiQueryMachineRules → FlowRuleNacosProvider.getRules → ConfigService.getConfig (3s timeout → empty)

Scenario matrix

Scenario Before PR After PR Purpose
Flow V2 query 200, 14-child SentinelApiClient chain, saveAll([]) 200, 2-child Nacos chain, saveAll([]) Prove silent bean swap

Trace evidence

Trace HTTP Write Downstream Result
before 200 saveAll([]) SentinelApiClient HTTP chain (6 nested) ApiProvider path, 14 child calls
after 200 saveAll([]) ConfigService.getConfig (3s timeout) NacosProvider path, empty data

Contract delta

Layer Before PR After PR Result
HTTP 200, same shape 200, same shape stable
Bean wiring flowRuleDefaultProvider = ApiProvider flowRuleDefaultProvider = NacosProvider silent swap
Persistence saveAll([]) saveAll([]) stable
Downstream SentinelApiClient HTTP Nacos ConfigService swapped
Data [] (no rules registered) [] (Nacos timeout) both empty, different cause
Time 221 ms 3070 ms 14× slower

Key trace delta

First meaningful divergence: FlowRuleNacosProvider.getRules().

Branch Trace shape
Before PR apiQueryMachineRulesFlowRuleApiProvider.getRulesSentinelApiClient.fetchFlowRuleOfMachine (6 nested) → saveAll([])
After PR apiQueryMachineRulesFlowRuleNacosProvider.getRulesConfigService.getConfig (3s timeout) → saveAll([])
Change #2 - DegradeController API contract break

The degrade controller query method drops ip/port from its signature (3 args → 1). The data source switches from SentinelApiClient.fetchDegradeRuleOfMachine to DegradeRuleNacosProvider.getRules. The mutation endpoint publishRules() changes from returning boolean to throws Exception (code-only — mutation paths not runtime-tested because they share the same Nacos publishConfig path proven by reads).

Behavior contract

# GET /degrade/rules.json?app=sentinel-dashboard&ip=127.0.0.1&port=8719 — runtime contract
- HTTP 200 OK  {success:true, data:[]}
+ HTTP 200 OK  {success:true, data:[]}   (silent — Nacos unreachable)
- method args: [app='sentinel-dashboard', ip='172.17.0.2', port=8719]  (3 args)
+ method args: [app='sentinel-dashboard']  (1 arg — ip/port dropped from signature)
- validation: AppManagement.isValidMachineOfApp(app, ip, port)
+ validation: REMOVED (no ip/port to validate)
- call tree: SentinelApiClient.fetchDegradeRuleOfMachine → executeCommand (direct HTTP)
+ call tree: DegradeRuleNacosProvider.getRules → ConfigService.getConfig (3s timeout)
- write: InMemoryRuleRepositoryAdapter.saveAll([])
+ write: InMemoryRuleRepositoryAdapter.saveAll([])   (retained under different parent path)
- mutation: publishRules returns boolean
+ mutation: publishRules throws Exception  (code-only)
- time: 8.8ms
+ time: 3010ms   (342x slowdown)
# first divergence: DegradeRuleNacosProvider.getRules()

Scenario matrix

Scenario Before PR After PR Purpose
Degrade query 200, 3 args, SentinelApiClient chain 200, 1 arg, Nacos provider Prove API contract break

Trace evidence

Trace HTTP Args Write Result
before 200 [app, ip, port] (3) saveAll([]) SentinelApiClient path
after 200 [app] (1) saveAll([]) Nacos provider path, empty data

Contract delta

Layer Before PR After PR Result
HTTP 200, same shape 200, same shape stable
Method signature (app, ip, port) (app) break
Validation isValidMachineOfApp removed break
Persistence saveAll([]) saveAll([]) stable (different parent path)
Downstream SentinelApiClient HTTP Nacos ConfigService swapped
Mutation contract boolean return throws Exception code-only break
Time 8.8 ms 3010 ms 342× slower

Key trace delta

First meaningful divergence: DegradeRuleNacosProvider.getRules().

Branch Trace shape
Before PR apiQueryMachineRulesSentinelApiClient.fetchDegradeRuleOfMachineexecuteCommandsaveAll([])
After PR apiQueryMachineRulesDegradeRuleNacosProvider.getRulesConfigService.getConfig (3s timeout) → saveAll([])
Change #3 - ParamFlowRuleController major simplification + write added

The most aggressive refactor: method signature drops ip/port (3 args → 1), checkIfSupported version check is removed, CompletableFuture async is replaced by synchronous Nacos, and repository.saveAll([]) is added to the query path (was not called on base).

Behavior contract

# GET /paramFlow/rules?app=sentinel-dashboard&ip=127.0.0.1&port=8719 — runtime contract
- HTTP 200 OK  {success:true, data:[]}
+ HTTP 200 OK  {success:true, data:[]}   (silent — Nacos unreachable)
- method args: [app, ip, port]  (3 args)
+ method args: [app]  (1 arg — ip/port dropped)
- pre-call: checkIfSupported (Sentinel client version check)
+ pre-call: REMOVED
- async: CompletableFuture.supplyAsync → SentinelApiClient chain
+ sync: ParamFlowRuleNacosProvider.getRules → ConfigService.getConfig (blocking 3s)
- write: no saveAll on query path
+ write: InMemoryRuleRepositoryAdapter.saveAll([]) ADDED
- call tree: 11 child calls
+ call tree: 2 child calls
- time: 11.7ms
+ time: 3009ms   (257x slowdown)
# first divergence: InMemoryRuleRepositoryAdapter.saveAll() added in AFTER

Scenario matrix

Scenario Before PR After PR Purpose
ParamFlow query 200, async, 11 child calls, no saveAll 200, sync, 2 child calls, saveAll added Prove API break + write delta

Trace evidence

Trace HTTP Args Write Async Result
before 200 [app, ip, port] none CompletableFuture 11 child calls
after 200 [app] saveAll([]) added sync 2 child calls

Contract delta

Layer Before PR After PR Result
HTTP 200, same shape 200, same shape stable
Method signature (app, ip, port) (app) break
Version check checkIfSupported removed break
Execution async CompletableFuture sync (blocking) changed
Persistence no saveAll saveAll([]) added added
Downstream SentinelApiClient HTTP Nacos ConfigService swapped
Time 11.7 ms 3009 ms 257× slower

Key trace delta

First meaningful divergence: InMemoryRuleRepositoryAdapter.saveAll() added in AFTER at position [2].

Branch Trace shape
Before PR apiQueryAllRulesForMachinecheckIfSupportedCompletableFuture.supplyAsyncSentinelApiClient chain
After PR apiQueryAllRulesForMachineParamFlowRuleNacosProvider.getRulesConfigService.getConfig (3s) → saveAll([])
Change #4 - AuthorityRuleController saveAll removed

The only controller where repository.saveAll([]) is removed from the query path. ip/port validation is retained (checkBasicParams still called), making this inconsistent with the other four controllers where saveAll is either retained or added. Rules fetched from Nacos are returned directly without caching in the in-memory repository.

Behavior contract

# GET /authority/rules?app=sentinel-dashboard&ip=127.0.0.1&port=8719 — runtime contract
- HTTP 200 OK  {success:true, data:[]}
+ HTTP 200 OK  {success:true, data:[]}   (silent — Nacos unreachable)
- method args: [app, ip, port]  (3 args — signature UNCHANGED, validation retained)
+ method args: [app, ip, port]  (3 args — signature UNCHANGED)
- validation: checkBasicParams → isValidMachineOfApp  (retained)
+ validation: checkBasicParams → isValidMachineOfApp  (retained)
- write: InMemoryRuleRepositoryAdapter.saveAll([]) called
+ write: saveAll REMOVED from query path
- call tree: SentinelApiClient.fetchAuthorityRulesOfMachine chain (14 child calls)
+ call tree: AuthorityRuleNacosProvider.getRules (4 child calls)
- time: 6.68ms
+ time: 3013ms   (450x slowdown)
# first divergence: AppManagement.getDetailApp() (environmental drift — hostname/version/heartbeat)
# structural change: AuthorityRuleNacosProvider.getRules replacing SentinelApiClient chain

Baseline note: The first divergence flagged by the trace comparison is AppManagement.getDetailApp() — environmental drift (hostname, version label, heartbeat timestamp differ between deploys). This is NOT a code regression; the real structural change is the provider swap deeper in the tree.

Scenario matrix

Scenario Before PR After PR Purpose
Authority query 200, saveAll called, 14 child calls 200, saveAll removed, 4 child calls Prove write delta + retained validation

Trace evidence

Trace HTTP Validation Write Result
before 200 checkBasicParams retained saveAll([]) called (0.04ms) SentinelApiClient path
after 200 checkBasicParams retained saveAll NOT called Nacos provider path, empty data

Contract delta

Layer Before PR After PR Result
HTTP 200, same shape 200, same shape stable
Method signature (app, ip, port) (app, ip, port) stable
Validation checkBasicParams + isValidMachineOfApp retained stable
Persistence saveAll([]) called saveAll removed removed
Downstream SentinelApiClient HTTP Nacos ConfigService swapped
Time 6.7 ms 3013 ms 450× slower

Key trace delta

First code divergence (after environmental drift): AuthorityRuleNacosProvider.getRules() replacing SentinelApiClient.fetchAuthorityRulesOfMachine.

Branch Trace shape
Before PR apiQueryAllRulesForMachinecheckBasicParamsisValidMachineOfAppSentinelApiClient chain → saveAll([])
After PR apiQueryAllRulesForMachinecheckBasicParamsisValidMachineOfAppAuthorityRuleNacosProvider.getRules (3s timeout) — no saveAll
Change #5 - SystemController cleanest migration

The best-implemented migration of the five: ip/port validation (checkBasicParams + isValidMachineOfApp) is retained, saveAll is retained, and SystemRuleNacosProvider post-processes Nacos results to set ip/port on each returned rule entity (code-verified — all runtime traces return data=[], so no entities were actually post-processed at runtime). Same silent-data-loss caveat applies when Nacos is unreachable.

Behavior contract

# GET /system/rules.json?app=sentinel-dashboard&ip=127.0.0.1&port=8719 — runtime contract
- HTTP 200 OK  {success:true, data:[]}
+ HTTP 200 OK  {success:true, data:[]}   (silent — Nacos unreachable)
- method args: [app, ip, port]  (signature UNCHANGED)
+ method args: [app, ip, port]  (signature UNCHANGED)
- validation: checkBasicParams → isValidMachineOfApp  (retained)
+ validation: same  (retained)
- provider: SentinelApiClient.fetchSystemRuleOfMachine
+ provider: SystemRuleNacosProvider.getRules → ConfigService.getConfig
- post-process: sets ip/port on each returned rule entity  (code-only)
+ post-process: same code path  (code-only — data=[] means no entities post-processed at runtime)
- write: InMemoryRuleRepositoryAdapter.saveAll([])
+ write: same  (retained)
- time: 8.5ms
+ time: 3011ms   (354x slowdown)
# first divergence: AppManagement.getDetailApp() (environmental drift — hostname/version/heartbeat)
# structural change: SystemRuleNacosProvider.getRules replacing SentinelApiClient chain

Scenario matrix

Scenario Before PR After PR Purpose
System query 200, validation + saveAll retained 200, validation + saveAll retained, Nacos provider Prove cleanest migration

Trace evidence

Trace HTTP Validation Write Post-process Result
before 200 retained saveAll([]) n/a (SentinelApiClient) SentinelApiClient path
after 200 retained saveAll([]) code-only (data=[]) Nacos provider path

Contract delta

Layer Before PR After PR Result
HTTP 200, same shape 200, same shape stable
Method signature (app, ip, port) (app, ip, port) stable
Validation checkBasicParams + isValidMachineOfApp retained stable
Persistence saveAll([]) saveAll([]) stable
Downstream SentinelApiClient HTTP Nacos ConfigService swapped
Post-processing n/a sets ip/port on entities code-only addition
Time 8.5 ms 3011 ms 354× slower

Key trace delta

First code divergence (after environmental drift): SystemRuleNacosProvider.getRules() replacing SentinelApiClient.fetchSystemRuleOfMachine.

Branch Trace shape
Before PR apiQueryMachineRulescheckBasicParamsisValidMachineOfAppSentinelApiClient.fetchSystemRuleOfMachinesaveAll([])
After PR apiQueryMachineRulescheckBasicParamsisValidMachineOfAppSystemRuleNacosProvider.getRules (3s timeout) → post-process → saveAll([])

Follow-Ups

Type Item Blocking
Merge blocker Make the Nacos server address configurable (application.properties) Yes
Merge blocker Surface Nacos connectivity failures as errors instead of returning empty data as success Yes
Merge blocker Restore ip/port params on DegradeController and ParamFlowRuleController (or version the path) Yes
Merge blocker Document the @Component('flowRuleDefaultProvider') bean swap on FlowControllerV2 or use explicit @Primary/@Qualifier wiring Yes
Merge blocker Resolve the saveAll inconsistency between AuthorityRuleController (removed) and ParamFlowRuleController (added) Yes
Merge blocker Add unit/integration tests for the 10 new Nacos classes (currently zero test files) Yes
Merge blocker Sign the CLA (GitHub currently reports mergeStateStatus=BLOCKED) Yes
Non-blocking Verify spring.cloud.nacos.config.namespace=public — in Nacos, public is a display name; the actual default namespace ID is the empty string No
Non-blocking Confirm the frontend flowV1flow sidebar change does not break operators relying on the V1 flow rule UI page No
Non-blocking Consider keeping checkIfSupported on ParamFlowRuleController to reject older Sentinel clients No

Recommendation

Request changes. The Nacos integration pattern is sound and SystemController demonstrates it can be done cleanly (validation + saveAll retained, results post-processed). However, the hardcoded private Nacos address makes every rule query fail silently in any environment that does not host 192.168.234.59:8848 — operators see success:true with data=[] and no error path. The API contract breaks on DegradeController and ParamFlowRuleController (3 args → 1) will break existing clients, the silent bean hijack on FlowControllerV2 is invisible without trace evidence, the saveAll inconsistency between AuthorityRuleController (removed) and ParamFlowRuleController (added) is a design smell, and the 10 new Nacos classes ship with zero test coverage. Address the merge blockers above and this PR is a reasonable Nacos backend for Sentinel Dashboard rule sync.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants