Skip to content

refactor(nacos): migrate from SDK v1 to v2, remove v1 dependency#982

Open
Aias00 wants to merge 8 commits into
apache:developfrom
Aias00:migrate-nacos-v2
Open

refactor(nacos): migrate from SDK v1 to v2, remove v1 dependency#982
Aias00 wants to merge 8 commits into
apache:developfrom
Aias00:migrate-nacos-v2

Conversation

@Aias00

@Aias00 Aias00 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrate all Nacos SDK usage from the deprecated v1.1.3 (2020) to v2.3.2, consolidating on a single version and eliminating dependency bloat and potential security issues.

Problem

The project had both github.com/nacos-group/nacos-sdk-go v1.1.3 and github.com/nacos-group/nacos-sdk-go/v2 v2.3.2 as dependencies. Only pkg/adapter/mcpserver/registry/nacos/ used v2; 5 other packages still used v1. The v1 SDK is outdated (2020) with known security issues and API incompatibilities.

Changes

Import path updates (all nacos-using files)

All imports changed from github.com/nacos-group/nacos-sdk-go/... to github.com/nacos-group/nacos-sdk-go/v2/...

Breaking API change: SubscribeCallback signature

  • v1: func(services []model.SubscribeService, err error)
  • v2: func(services []model.Instance, err error)

The model.SubscribeService type no longer exists in v2 — the callback receives []model.Instance directly.

Code simplifications

  • Removed generateInstance() helper functions (3 files) that converted SubscribeService → Instance — no longer needed since v2 callback provides Instance directly
  • Removed fromSubscribeServiceToServiceInstance() in springcloud/nacos.go, now using fromInstanceToServiceInstance() directly
  • Added @@ group prefix stripping for ServiceName in springcloud callback (previously handled by the removed conversion function)
  • Removed references to model.Instance.Valid field (removed in v2 SDK)

Test updates

  • Updated mock and test data from SubscribeService to Instance type
  • Added ServerHealthy() and CloseClient() methods to test mock to satisfy v2's INamingClient interface

Dependency cleanup

  • go.mod: removed github.com/nacos-group/nacos-sdk-go v1.1.3 direct dependency
  • go.sum: cleaned up v1 entries

Files Modified (13 files, net -40 lines)

Category File Change
Import-only pkg/remote/nacos/client.go import paths → v2
Import-only pkg/configcenter/nacos_load.go import paths → v2
Import-only pkg/adapter/dubboregistry/registry/nacos/registry.go import paths → v2
Import-only pkg/adapter/dubboregistry/registry/nacos/application_listener.go import paths → v2
Import-only pkg/adapter/dubboregistry/registry/nacos/interface_listener.go import paths → v2
Callback sig application_service_listener.go []SubscribeService[]Instance, remove generateInstance()
Callback sig service_listener.go []SubscribeService[]Instance, remove generateInstance(), drop .Valid
Callback sig listener.go []SubscribeService[]Instance, remove generateInstance(), drop .Valid
Callback sig springcloud/nacos.go []SubscribeService[]Instance, remove fromSubscribeServiceToServiceInstance(), add @@ prefix strip
Tests listener_test.go Mock: SubscribeServiceInstance, add ServerHealthy() + CloseClient()
Deps go.mod / go.sum Removed v1.1.3 dependency

Verification

  • go build ./... — passes
  • go vet ./... — no warnings
  • go mod tidy — clean, only v2 remains
  • go test ./pkg/adapter/llmregistry/registry/nacos/... — all 8 tests pass
  • go test ./pkg/adapter/mcpserver/registry/nacos/... — passes
  • go test ./pkg/configcenter/... — passes
  • ✅ No remaining v1 imports in source code

Migrate all Nacos SDK imports from the deprecated v1.1.3 (2020) to v2.3.2,
consolidating on a single version and eliminating dependency bloat.

Key changes:
- Updated all import paths from nacos-sdk-go to nacos-sdk-go/v2
- Changed SubscribeCallback signature from []SubscribeService to []Instance
  (v2 delivers Instance directly in callbacks, no manual conversion needed)
- Removed generateInstance() helper functions that converted SubscribeService
  to Instance (3 files: service_listener.go, listener.go, nacos.go)
- Removed fromSubscribeServiceToServiceInstance() in springcloud/nacos.go,
  now using fromInstanceToServiceInstance() directly in the callback
- Added @@ group prefix stripping for ServiceName in springcloud callback
  (previously handled by removed conversion function)
- Removed references to model.Instance.Valid field (removed in v2)
- Added ServerHealthy() and CloseClient() methods to test mock to satisfy
  v2's INamingClient interface
- Updated all test data from SubscribeService to Instance type

Files modified:
  pkg/remote/nacos/client.go                     (imports only)
  pkg/configcenter/nacos_load.go                 (imports only)
  pkg/adapter/dubboregistry/registry/nacos/      (5 files)
  pkg/adapter/springcloud/servicediscovery/nacos/ (1 file)
  pkg/adapter/llmregistry/registry/nacos/        (3 files)
  go.mod / go.sum                                (v1 removed)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 16, 2026 11:50

Copilot AI left a comment

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR migrates the codebase from github.com/nacos-group/nacos-sdk-go v1 to github.com/nacos-group/nacos-sdk-go/v2, updating imports and adapting to the v2 subscription callback API.

Changes:

  • Switched Nacos SDK imports across adapters/config/remote clients from v1 to v2.
  • Updated Nacos subscribe callbacks from []SubscribeService to []Instance and removed now-unneeded conversion helpers.
  • Updated unit tests and mocks to match the v2 client interfaces.

Reviewed changes

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

Show a summary per file
File Description
pkg/remote/nacos/client.go Updates Nacos SDK imports to v2 for naming client usage.
pkg/configcenter/nacos_load.go Updates Nacos SDK imports to v2 for config client usage.
pkg/adapter/springcloud/servicediscovery/nacos/nacos.go Adapts subscribe callback signature to v2 Instance and adjusts service name parsing logic.
pkg/adapter/llmregistry/registry/nacos/registry.go Updates Nacos SDK imports to v2 for naming registry integration.
pkg/adapter/llmregistry/registry/nacos/listener.go Updates callback types to v2 Instance and removes SubscribeServiceInstance conversion.
pkg/adapter/llmregistry/registry/nacos/listener_test.go Updates mocks/tests for v2 types and adds methods required by updated client interfaces.
pkg/adapter/dubboregistry/registry/nacos/service_listener.go Updates callback types to v2 Instance and removes conversion helper.
pkg/adapter/dubboregistry/registry/nacos/registry.go Updates Nacos SDK imports to v2 for Dubbo registry integration.
pkg/adapter/dubboregistry/registry/nacos/interface_listener.go Updates Nacos SDK imports to v2 for listener integration.
pkg/adapter/dubboregistry/registry/nacos/application_service_listener.go Updates callback types to v2 Instance and uses Instance directly.
pkg/adapter/dubboregistry/registry/nacos/application_listener.go Updates Nacos SDK imports to v2 for application listener integration.
go.mod Removes v1 nacos-sdk-go requirement and keeps v2.
go.sum Removes v1.1.3 checksum entries and keeps v2 entries.
Comments suppressed due to low confidence (1)

pkg/adapter/dubboregistry/registry/nacos/application_service_listener.go:1

  • handleServiceName (per the provided context) returns "" when there is no "@@" in the input. This code unconditionally overwrites services[i].ServiceName, which can blank out valid service names and break downstream logic that relies on Instance.ServiceName. Fix by changing handleServiceName to return the original serviceName when no group prefix is present, or only overwrite ServiceName when the split actually finds a suffix.
/*

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

Comment on lines +121 to +128
// v2 subscribe callback receives Instance directly (was SubscribeService in v1)
// ServiceName may contain group prefix like "DEFAULT_GROUP@@service-name", strip it
serviceName := service.ServiceName
if tmp := strings.Split(serviceName, "@@"); len(tmp) == 2 {
serviceName = tmp[1]
}

instance := fromInstanceToServiceInstance(serviceName, service)

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.

Already addressed in commit 233d147b — the @@ prefix strip now uses strings.Cut(serviceName, "@@") (split once, no extra allocation). See nacos.go Callback.

Comment on lines 129 to 130
key := instance.GetUniqKey()
newInstanceMap[instance.GetUniqKey()] = instance

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.

Already addressed in commit 233d147bkey := instance.GetUniqKey() is now computed once and reused for the map write (newInstanceMap[key] = instance).

Aias00 and others added 2 commits June 16, 2026 19:53
…qKey call

- Replace strings.Split with strings.Cut for group prefix stripping
  (cleaner idiom, avoids unnecessary allocation)
- Fix duplicate instance.GetUniqKey() call by reusing key variable

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 27.28%. Comparing base (331735d) to head (ac36aad).
⚠️ Report is 14 commits behind head on develop.

Files with missing lines Patch % Lines
...try/registry/nacos/application_service_listener.go 0.00% 2 Missing ⚠️
pkg/adapter/llmregistry/registry/nacos/registry.go 66.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #982      +/-   ##
===========================================
+ Coverage    26.29%   27.28%   +0.98%     
===========================================
  Files          275      274       -1     
  Lines        21985    21993       +8     
===========================================
+ Hits          5782     6000     +218     
+ Misses       15590    15370     -220     
- Partials       613      623      +10     
Flag Coverage Δ
unittests 27.28% <92.30%> (+0.98%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Aias00 added 2 commits July 14, 2026 22:30
- Add tests for handleServiceName function (@@ prefix stripping)
- Add tests for toNacosInstance function
- Add tests for Callback skipping disabled instances
- Add tests for fromInstanceToServiceInstance function
- Add tests for generateURL function

Covers the new code paths from the v1 to v2 SDK migration.
Split imports into separate blocks:
- Standard library imports
- Third-party imports
"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
"github.com/nacos-group/nacos-sdk-go/vo"
"github.com/nacos-group/nacos-sdk-go/v2/clients"

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] 补齐 Nacos 1.x 与 gRPC 端口的升级门禁

这不是纯 API 替换。Nacos 官方兼容说明明确:2.x 客户端不兼容 1.x 服务端,并且客户端会通过主端口 +1000(默认 8848 → 9848)建立 gRPC;存在防火墙、容器映射或代理时必须额外开放该 TCP 端口。当前仓库的 Nacos 配置/文档仍只要求 8848,docs/ai/registry*.md 甚至仍示例导入 v1 vo,也没有声明最低服务端版本,因此现有 Nacos 1.x 或只开放 8848 的部署会在升级后运行期失联。请保留兼容路径,或明确最低 Nacos 2.x、补充 9848/TCP 转发与升级文档,并增加真实 Nacos 2.x 的连接验证。官方依据:https://github.com/nacos-group/nacos-group.github.io/blob/5e9d9ca33caeac97ebef7146f6f3f2b3a82793f6/src/content/docs/v2/en/upgrading/200-compatibility.md

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.

已处理(commit 0e94c1ee)。未保留 v1 兼容回退路径(会重新引入 v1 依赖,与本次迁移目的相悖),改为按你给的第二个方案:

  • docs/ai/registry.md / registry_CN.md 顶部新增 Nacos 2.x 服务端要求 说明:v2 客户端不兼容 1.x 服务端,最低要求 2.x(建议 2.2.0+)。
  • 明确 gRPC 端口 = 主端口 + 1000(8848→9848),并要求防火墙/容器映射/反代场景同时开放 8848/TCP9848/TCP,否则运行期失联。docs/ai/mcp/mcp.md / mcp_CN.md 同步补充。
  • 修正示例中过时的 v1 导入 nacos-sdk-go/vonacos-sdk-go/v2/vo

真实 2.x 连接验证受限于 CI 无 Nacos 实例,未新增 e2e;关闭路径已用 mock 断言。

return true
}

func (m *mockNacosClient) CloseClient() {}

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] 生产关闭路径没有消费 v2 的 CloseClient

这里为满足 v2 INamingClient 补了空实现,但生产生命周期仍只做 listener/unsubscribe:LLM DoUnsubscribe、Spring Cloud 的 Nacos 包装器以及配置中心都没有调用 CloseClient()。v2 的实现会在该方法中执行 gRPC Shutdown() 并取消内部 context;仓库现有 MCP v2 适配器也在关闭时显式关闭 naming/config client。当前遗漏会让 Stop/Apply 或优雅退出后的连接、重试协程继续存活。请在所有本次迁移的客户端创建点补齐可达的关闭接口,在取消订阅后调用它,并用 mock 断言关闭发生。

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.

已处理(commit 0e94c1ee)。在所有本次迁移的客户端创建点补齐了可达的关闭接口,取消订阅/停止后调用 CloseClient(),并用 mock 断言关闭发生:

  • llmregistryNacosRegistry.DoUnsubscribe 在停掉 listener 后调 client.CloseClient();抽出 newNacosRegistryWithClient 供测试注入 mock,新增 TestDoUnsubscribeClosesClient 断言 CloseClient 被调用 1 次。
  • springcloudnacosServiceDiscovery.Unsubscribe 末尾调 n.client.Close();client 字段抽象为 nacosNamingClient 接口(*nacos.NacosClient 仍满足),mock 记录 Close,新增 TestUnsubscribeClosesClient
  • configcenter:给 ConfigClient/Load 接口与 ConfigManagerClose()NacosConfig.Close 透传 SDK CloseClientTestDefaultConfigLoad_Close 用 mock 断言转发。
  • dubboregistry:原 DoUnsubscribepanic("implement me"),改为真实关闭(Listener.Close + client.CloseClient),与 ZKRegistry 对齐。
  • pkg/remote/nacosNacosClient.Close 暴露 namingClient.CloseClient()

SDK 的 CloseClient 内部有 isClosed 守卫,幂等,重复 Stop 安全。

if _, after, ok := cutServiceName(serviceName, "@@"); ok {
serviceName = after
}
assert.Equal(t, tt.expected, serviceName)

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.

[P2] 这条测试没有执行生产 Callback

当前用测试文件里的 cutServiceName 复制了一遍分隔逻辑,生产代码即使删除前缀处理、改错分隔符或没有把结果传给 listener,这条测试仍会通过。请让 mock 记录收到的 ServiceInstance,直接调用 nacosServiceDiscovery.Callback 并断言 ServiceName,然后删除这份复制实现。

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.

已处理(commit 0e94c1ee)。删除了复制的 cutServiceName 实现。TestCallback_ServiceNameWithGroupPrefix 现在直接调用生产 nacosServiceDiscovery.Callback:mock listener (mockServiceEventListener) 记录收到的 ServiceInstance,断言 ServiceName 已剥离 @@ 前缀。这样若生产代码删掉前缀处理、改错分隔符或没把结果传给 listener,测试都会失败。

Aias00 and others added 3 commits July 16, 2026 06:02
…ack test

Address AlexStocks' [P1]/[P2] review on PR apache#982 (Nacos SDK v1→v2 migration).

[P1] Close path consumes v2 CloseClient
The v2 clients hold a gRPC connection + internal retry goroutines that survive
Unsubscribe; the migration added empty CloseClient() to test mocks but never
called it in production. Wire CloseClient into every migrated client's
shutdown path and assert with mocks:
- llmregistry: NacosRegistry.DoUnsubscribe closes the naming client after
  stopping the listener (newNacosRegistryWithClient extracted for mock
  injection; TestDoUnsubscribeClosesClient).
- springcloud: nacosServiceDiscovery.Unsubscribe closes the naming client;
  client field abstracted behind a nacosNamingClient interface so a mock can
  record Close (TestUnsubscribeClosesClient).
- configcenter: add Close() to ConfigClient/Load interfaces + ConfigManager;
  NacosConfig.Close delegates to the SDK (TestDefaultConfigLoad_Close).
- dubboregistry: replace the panicking DoUnsubscribe with a real close
  (listener.Close + CloseClient), matching ZKRegistry.
- pkg/remote/nacos: NacosClient.Close exposes namingClient.CloseClient.

[P1] Nacos 2.x compatibility & docs
v2 client is incompatible with 1.x servers and uses gRPC port = main port +
1000 (8848→9848). Document the minimum server version (2.x) and the 9848/TCP
requirement in docs/ai/registry(.md/_CN.md) and docs/ai/mcp/mcp(.md/_CN.md),
and fix the stale v1 SDK import (nacos-sdk-go/vo → /v2/vo) in the examples.

[P2] springcloud test drives production Callback
Replace the copied cutServiceName separator logic with a test that calls the
real nacosServiceDiscovery.Callback and asserts the stripped ServiceName via a
listener mock that records received ServiceInstances.

Copilot nits (strings.Cut, duplicate GetUniqKey) were already fixed in
233d147; no further change needed.

Verified: go build ./..., go vet, go test on all affected packages.

Co-Authored-By: Claude <noreply@anthropic.com>
CI's imports-formatter runs with the default -bl=true (split import modules
with a blank line), so testify/assert and the pkg/* imports must be in
separate groups. The previous commit put them in one group, failing the
"Check Import Formatting" job on PR apache#982.

Co-Authored-By: Claude <noreply@anthropic.com>
Add unit tests for the v2 close paths introduced in 0e94c1e so the new
shutdown code is not uncovered (Codecov flagged 22 missing lines). Each new
Close/DoUnsubscribe method is now exercised with a mock that records the
close call:

- dubboregistry: mockNamingClient + TestDoUnsubscribeClosesClient (covers
  listener close, svc-listener close loop, and client.CloseClient) and
  TestDoUnsubscribe_NoListener (error path that replaced the panic).
- pkg/remote/nacos: TestNacosClient_Close (delegates to namingClient.CloseClient).
- configcenter: TestNacosConfig_Close (delegates + nil-client no-op).
- pkg/config: TestConfigManager_Close (forwards to loader + nil-load no-op).

All new Close methods report 100% function coverage locally. Formatter run
with CI's -bl=true to keep import grouping consistent.

Co-Authored-By: Claude <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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.

4 participants