Nacos Agent Discovery Java SDK 场景矩阵:测试优先契约、集成测试分层与实现轨迹
【免费下载链接】nacosan easy-to-use dynamic service discovery, configuration and service management platform for building AI cloud native applications.项目地址: https://gitcode.com/GitHub_Trending/na/nacos
导读
本文围绕 Nacos 仓库中的 AGENT_DISCOVERY_SDK_IT_SCENARIOS.md 展开,它是第一个AgentDiscoveryService(协议无关的 Agent 发现服务)实现的"测试优先契约(test-first contract)与执行轨迹(execution trace)":在生产代码落地之前先固定场景清单,再由配套的 Java SDK 集成测试(IT)与单元测试(UT)逐条守护。读完本文,你将掌握:Agent 发现的测试分层体系(IT / Directed IT / UT / 延后项);Search、Discover、本地轮询订阅、完整端点发布、部分/完整注销、心跳重连与重放(redo)等核心工作流的精确预期;18 个稳定单机工作流的覆盖范围;以及真实单机进程重启场景下 gRPC 重连与 HTTP50404恢复的验证方式。文章同时结合 AgentDiscoveryService.java、AgentDiscoveryServiceJavaSdkITCase.java 等源码给出实现级证据。
一、背景:为什么需要一份"场景矩阵"式测试契约
Agent 发现涉及命名空间、版本演进、标签选择、端点发布/注销、多传输(gRPC/HTTP)以及断线重连等多个正交维度,单纯靠"参数笛卡尔积"式的用例堆砌既不可维护也无法表达真实操作次序带来的语义差异。因此该矩阵采用**操作次序等价类(operation-order equivalence classes)**作为组织单位:例如对单个发布方而言,Register 是完整替换语义,因此有意义的次序只有"端点先于定义"、"定义先于端点"、"在已上线版本上做替换"三类。
矩阵本身的定位是:
- 测试优先契约:场景在生产代码之前固定,并与最终落地的测试一同维护;
- 每个场景分配到稳定的单机 Java SDK IT、确定性单元测试,或两者兼有;
- 明确范围边界:首个实现采用主动 Discover 轮询(active Discover polling),不新增服务端 Watch 请求、Push 载荷、ACK 或 Watch 能力。
对应的测试载体为 AgentDiscoveryServiceJavaSdkITCase.java(1894 行),它仅通过公共AiService契约驱动被测行为,AgentMaintainerService只作为外部搭建与清理客户端使用(见文件头注释 L96-L105)。
二、测试分层图例(Test-Layer Legend)
矩阵为每个场景标注了验证层级,含义如下:
| 测试层级 | 含义 |
|---|---|
| IT | 通过公共 Java SDK 针对单机服务端运行。 |
| Directed IT | 通过公共 SDK 客户端运行,同时外部 harness 重启真实单机服务端。由于普通共享服务端 IT 不能停机,该层级默认不参与(opt-in)。 |
| UT | 使用确定性的传输、调度器、重连或故障注入。 |
| IT + UT | 公共工作流在 IT 中运行,本地不变量由单元测试验证。 |
| Deferred | 不在本阶段范围内,记录原因且不新增生产代码。 |
关键约束在文档中有明确表述:共享服务端 CI 运行永远不会被测试停机;调度器、监听器失败、传输错误、能力(ability)、心跳/50404、回滚以及重放竞态等都属于确定性 UT 职责。
三、已落地的单机实现证据(18 个稳定工作流)
AgentDiscoveryServiceJavaSdkITCase执行了以下稳定的公共工作流,每个方法对应一个场景组:
| IT 方法 | 覆盖的场景组 |
|---|---|
shouldInteroperateWithLegacyA2aSdk | 旧 A2A SDK 定义发布、Console 与 RAD 规范读取、重复发布不覆盖、旧版 exact-Version Endpoint 注册进规范 Runtime Registry(Beta 阶段不向历史 Naming 服务双写)、Console Runtime Snapshot 与旧 SERVICE 查询一致、Version 2 预注册、省略选择器多版本聚合与显式 latest 隔离、规范版本发布、旧版 latest 订阅收敛。 |
shouldSearchDiscoverAndIsolateNamespaces | 默认/自定义命名空间;默认、单个、组合、空、分页 Search;latest/exact/label Discover;组合过滤;调用方不可变性;显式与不匹配的命名空间绑定;命名空间隔离的发布。 |
shouldReplaceAndPartiallyDeregisterCompletePublications | 完整注册、相同内容的幂等、替换收敛、规范自然键的部分注销、未知/重复注销为 no-op、最终注销、协议隔离。 |
shouldAggregateIndependentSdkPublishers | 两个 SDK 身份贡献同一自然键、最后贡献者移除。 |
shouldDiscoverPreRegistrationAndPollUntilAgentAppears | 预注册、Discover 缺失、先订阅后创建、完整回调快照、退订、退订后抑制。 |
shouldPollExistingAgentOnlyWhenCompleteFingerprintChanges | 订阅已有当前值、Runtime source-revision 替换事件、指纹未变时的回调去重。 |
shouldTrackVersionEvolutionAcrossRegistrationOrders | Version 1 定义优先、Version 2 端点优先、Version 3 定义优先、latest/exact/label 订阅、目录顺序、离线/上线后的 latest 重算。 |
shouldSeparateDefaultRolloutPoolFromExplicitLatest | 两个独立发布方并发保有 exact Version 1 与 Version 2 端点;省略选择器使用 latest 版本元数据但聚合所有在线版本的兼容端点;显式label=latest仅含 latest;验证 Version 2 端点注册前的空窗期、注册后的合并池、Version 1 下线后的移除、绑定来源、两种选择器的轮询回调去重。 |
shouldApplyPublicationRangeAcrossOnlineVersions | 含端点的版本区间、替换为不同区间、精确版本匹配、非匹配版本排除。 |
shouldDeregisterActiveHttpPublicationDuringIdempotentShutdown | 活跃及重复 SDK 关闭期间的 HTTP 发布清理。 |
shouldKeepHttpAndGrpcDiscoverySemanticsEquivalent | Search、Discover、HTTP 发布、gRPC 观察、注销的传输等价性。 |
shouldUseGrpcForAutoWhenInitialConnectionIsAvailable | AUTO 同步启动(有可用协商 gRPC 连接),随后 Search、端点发布、Discover、注销。 |
shouldFallbackAutoToHttpWhenGrpcNeverLeavesStarting | 故意不可达的 gRPC 端口使连接停留在 STARTING,AUTO 立即通过 HTTP 完成 Search、轮询订阅、发布、注销;显式 HTTP 与 gRPC 无关;显式 GRPC 无回退且失败。 |
shouldKeepSearchProjectionLifecyclePaginationAndTransportParity | gRPC/HTTP Search 在组合类型过滤、大小写敏感名称、稳定的首页/中页/末页/越界页码、Runtime Endpoint 不建索引、两版本 latest/目录收敛、单版本下线收敛、全部版本下线后移除等方面的等价性;同一组终态断言可复用于AUTO、INDEX、SCAN服务端运行。 |
shouldEnforceConfiguredLocalSubscriptionCapacityAndReuseSlot | 工作流配置的轮询订阅容量、幂等重复准入、超限同步拒绝(缓存之前)、退订后槽位复用。 |
shouldEnforceConfiguredLocalPublicationCapacityAndReuseSlot | 工作流配置的 SDK 发布软水位、从下方整批跨越、超水位幂等替换与新身份拒绝、注销后槽位复用。 |
shouldSurfaceServerPublicationCapacityAndStopRejectedRedo | 工作流配置的服务端权威发布软水位、从下方整批跨越、远端超限异常映射、被拒重放清理、注销后容量复用。 |
shouldRejectInvalidBoundariesBeforeRemoteMutation | Null、页边界、重复过滤条件/自然键、命名空间不匹配、引用歧义、非法协议/URI/传输/版本/区间、空发布、服务端持有健康状态、非法注销载荷、未知本地 no-op、not-found 映射。 |
补充事实:同样的 18 个工作流在默认 JSON adapter 与jackson3下均通过;既有AiServiceJavaSdkITCase作为兼容性回归一起运行。可选的shouldRestoreGrpcAndHttpPublicationsAndPollingAfterRealServerRestart工作流(见源码 L968-L1118)还针对由外部 harness 停止/重启的真实单机进程通过:它同时保有独立的 gRPC 与 HTTP 发布方及轮询订阅,因此重启后的服务端必须同时恢复 gRPC 重连重放与 HTTPHTTP_CLIENT_NOT_FOUND重新注册。
四、工厂、命名空间与生命周期
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
通过AiFactory创建不带命名空间的AiService | Agent 操作绑定到public。 | IT |
创建绑定public与自定义命名空间的服务 | Search、Discover、订阅、发布保持命名空间隔离。 | IT + UT |
| Request/Batch 省略命名空间 | SDK 复制值并注入绑定命名空间,不改动调用方输入。 | IT + UT |
| Request/Batch 携带相同命名空间 | 请求成功且调用方输入不变。 | UT |
| Request/Batch 携带另一个非空命名空间 | SDK 在传输调用前本地拒绝。 | IT + UT |
| 调用后复用调用方的列表、映射、引用、过滤器、端点 | SDK 状态与线上载荷与后续调用方变更隔离。 | UT |
| 无订阅/发布时关闭 | 轮询、心跳、HTTP 资源、gRPC 资源干净停止。 | IT + UT |
| 有活跃订阅/发布时关闭 | SDK 取消轮询与心跳,并尽力注销每个完整发布。 | IT + UT |
| 重复关闭 | 无重复回调、未受控异常或泄漏任务。 | UT |
grpcAgent 传输模式 | 初始 gRPC 连接同步尝试;连接不可用时持续重试,Agent 操作绝不回退 HTTP。 | IT + UT |
httpAgent 传输模式 | Agent 操作不依赖初始 gRPC 启动;配置的 gRPC 端口不可达时仍可用。 | IT + UT |
auto+ 可用协商 gRPC | Agent 操作优先 gRPC。 | IT + UT |
auto+ gRPC 停留在STARTING | 公共请求不等待后台探测,通过 HTTP 成功,仅在重试预算耗尽且一次 HTTP 成功后落定 HTTP。 | IT + UT |
| 非法、带空白或未知传输模式 | 工厂创建本地失败,抛出受控非法参数异常。 | UT |
实现证据:
- 传输模式由 AgentTransportMode.java 定义,
GRPC/HTTP/AUTO对应属性值grpc/http/auto,fromValue大小写不敏感,非法值抛IllegalArgumentException(L65-L72)。 - 属性键集中在 AiConstants.java:
AI_TRANSPORT_MODE = "nacosAiTransportMode"(L79)、AI_REQUEST_TIMEOUT = "nacosAiRequestTimeout"(L87),以及发布/订阅容量键nacosAiAgentEndpointMaxPublications(默认 100,L105-L114)与nacosAiAgentDiscoveryMaxSubscriptions(默认 300,L111-L116)。 AiService通过 AiFactory.createAiService(Properties) 反射创建com.alibaba.nacos.client.ai.NacosAiService,失败时抛CLIENT_INVALID_PARAM。
五、Search(目录搜索)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| 仅默认分页输入的 Search | 返回可见启用且拥有在线 latest 版本的 Agent,分页稳定。 | IT |
按字面量agentNameContains搜索 | 仅返回大小写敏感的字面包含匹配;%、_、\不作为数据存储通配符解释。 | OpenAPI IT + Java SDK IT |
按多个tagsAll搜索 | 每个返回的 Agent 都包含全部请求标签。 | IT |
按多个protocolsAny搜索 | 每个返回的 Agent 至少包含一个请求协议。 | IT |
| 组合名称、标签、协议与分页 | 除protocolsAny外过滤器以 AND 语义组合,页码元数据正确。 | IT |
| 空命名空间或不匹配过滤器的 Search | 返回成功的空Page。 | IT |
| Null 请求、非法页码、重复过滤值或非法协议 | 抛受控本地非法参数异常且不发请求。 | UT |
gRPC ability 为SUPPORTED | 恰好发送一次 Search RPC,返回类型化分页。 | UT |
gRPC ability 为NOT_SUPPORTED或UNKNOWN | 本地抛 unsupported 错误,无 legacy 或 HTTP 回退。 | UT |
| HTTP 传输 | 使用文档化的 GET 查询、auth 资源与稳定的 HTTP Client id。 | IT + UT |
| AUTO 读且 gRPC 当前不可用 | 立即选 HTTP,不等待异步重连。 | IT + UT |
| AUTO 读遇到 gRPC 连接类失败 | 读可经 HTTP 重试;明确的业务错误不回退。 | UT |
从 AgentDiscoveryService.java 看,searchAgents(AgentSearchRequest)返回Page<AgentCatalogEntry>(L49-L54),是@Since("3.3.0")的公共 API。测试侧对分页/目录收敛的断言见 AgentDiscoveryServiceJavaSdkITCase.java 中searchNamesByPage(L1792-L1808)与waitForCatalog(L1775-L1785)等辅助方法。
六、Discover(发现快照)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| 无 Version/label 的 Discover | 定义元数据从 latest 解析,Runtime Endpoints 聚合与每个在线版本兼容的绑定。 | IT + UT |
显式label=latest的 Discover | 定义元数据与 Runtime Endpoints 都限制在当前 latest 版本。 | IT + UT |
| 精确 Version 与自定义 label 引用 | 每个引用只解析该精确解析版本的元数据与端点。 | IT |
| 无 Filter 的 Discover | 保留所有允许的调用接口与端点来源。 | IT |
| 按 protocol/protocolVersion 过滤 | 保留匹配接口;无匹配返回callInterfaces=[]。 | IT |
| 按端点来源过滤 | 保留匹配接口;无匹配来源返回endpointSets=[]。 | IT |
| 按 transport/metadata 过滤 | 保留匹配端点集;无匹配端点返回endpoints=[]。 | IT |
| 组合全部过滤维度 | 完整类型化结果在文档化层级被过滤。 | IT |
| Runtime Endpoint 不健康 | Discover 保留它并标记healthy=false;不发生隐式选择。 | IT |
| 目标不存在、被禁用或无可见在线版本 | 抛受控 not-found 异常。 | IT |
| Version 与 label 同时设置,或引用/过滤非法 | SDK 本地拒绝且不改输入。 | IT + UT |
| gRPC ability 缺失或未知 | 本地抛 unsupported 错误,不发远程请求。 | UT |
| HTTP 与 gRPC 传输等价 | 两种传输对同一服务端状态返回等价类型化快照。 | IT |
| AUTO + 可用或从未连接 gRPC | 分别经 gRPC 或立即 HTTP 路由返回同一类型化快照。 | IT + UT |
API 层面对应discoverAgent(AgentReference)与discoverAgent(AgentReference, AgentDiscoveryFilter)两个重载(AgentDiscoveryService.java)。测试中sourceEndpoints(...)帮助方法按协议与EndpointSource(DECLARED/RUNTIME)抽取端点集(L1690-L1708)。
七、定义与版本演进(Definition And Version Evolution)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| 发布 Version 1 后注册其精确端点 Batch | 省略、显式 latest、精确版本 Discover 都返回 Version 1 与 Runtime Endpoint。 | IT |
| 在 Version 2 存在前注册精确 Version 2 Batch | 注册成功;latest 仍是 Version 1 可发现,但仅 Version 2 的端点被过滤掉。 | IT |
| 注册其端点前发布 Version 2 | Search 与两种选择模式使用 Version 2 元数据;省略 Discover 保留 Version 1 端点,显式 latest 返回空 Runtime 集。 | IT + UT |
| 从独立于 Version 1 的发布方注册 Version 2 | 省略 Discover 返回带绑定的 Version 1 与 Version 2 端点;显式 latest 仅返回 Version 2。 | IT + UT |
| Version 1 下线而 Version 2 在线 | 省略 Discover 移除仅 Version 1 的端点;显式 latest 不变且不发重复回调。 | IT + UT |
| Version 1 保留自定义 label 而 latest 前进 | Label Discover 停留在 Version 1,不隐式跟随 latest。 | IT |
| 自定义 label 从 Version 1 移到 Version 2 | Label Discover 与匹配的轮询订阅原子地移到 Version 2。 | IT |
| 注册精确 Batch 前发布 Version 3 | 显式 latest Discover 变为 Version 3 且 Runtime 集为空;省略 Discover 保留与较旧在线版本兼容的端点;后续 Register 改变相关 Runtime source-revision。 | IT |
| latest 在后续版本中前进时保持精确 Version 1 订阅 | 不收版本变更回调;仅当 Version 1 内容或匹配 source-revision 变化时才变更。 | IT + UT |
| 省略选择器订阅经历 latest 变更 | 立即收到 latest 元数据,但保留旧在线版本端点直至其版本下线。 | IT + UT |
| 显式 latest 订阅经历端点替换与版本变更 | 严格切到新 latest 池(含刻意空窗期),并抑制未变化的轮询。 | IT + UT |
| 多个在线版本后的 Search | 单条目录条目按 SemVer 降序列出所有在线版本并报告当前 latest。 | IT |
| 注册/注销 Runtime 端点而不改定义 | Search 目录字段与版本成员关系不变,因为 Runtime Endpoints 不建索引。 | OpenAPI IT + Java SDK IT |
| 两个在线版本下线其一,再下线最后一个 | 目录先收敛到剩余版本,随后 Agent 完全退出 Search。 | OpenAPI IT + Java SDK IT |
| 发布区间跨多个版本 | 每个匹配的 exact/latest Discover 看到共享端点;不匹配版本看不到。 | IT + UT |
| 下线后又将当前 latest 上线,另一版本仍在线 | Search/Discover 遵循服务端重算的 latest,不改发布方意图。 | IT |
该小节在测试源码中的典型映射是shouldTrackVersionEvolutionAcrossRegistrationOrders(L740-L837):它依次验证 Version 1 定义优先、Version 2 端点优先、Version 3 定义优先,以及offline/online后的 latest 重算。
八、本地轮询订阅(Local Polling Subscription)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| 订阅已存在目标 | 方法返回当前快照并保留一条轮询记录。 | IT + UT |
| 目标存在前订阅 | 返回null,保留轮询,目标出现后投递完整快照。 | IT + UT |
| 重复 not-found 轮询 | 不投递空快照,订阅保持活跃。 | UT |
| 轮询返回相同 Version、digest 与 source-revisions | 不投递重复监听器事件。 | IT + UT |
| 解析版本变化 | 投递一次完整替换事件。 | IT + UT |
contentDigest变化 | 投递一次完整替换事件。 | UT |
任一sourceRevision变化 | 投递一次完整替换事件。 | IT + UT |
| Runtime 绑定变化而端点载荷与健康不变 | v2 Runtime revision 变化,因为发现可见的绑定来源属于快照的一部分。 | UT |
| Filter 产生类型化空结果 | 结果被缓存并可替换更早的非空快照。 | UT |
| 相同引用/过滤与两个监听器实例 | 监听器身份隔离,两者都收到变化。 | UT |
| 用相同监听器身份重复订阅 | 每个变化只保留一条轮询记录与一次回调。 | UT |
| 达到配置的本地订阅上限 | 最后被接纳的订阅保持活跃;由于当前 API 每次调用安装一个订阅,下一个不同键在 Discover/缓存插入/调度之前抛CLIENT_OVER_THRESHOLD。未来批量的 Wire Watch 操作必须基于操作前水位整体准入或整体拒绝整批,不得部分缓存。 | IT + UT |
| 在上限处退订后再订阅另一键 | 释放的槽位立即复用,新订阅正常调度。 | IT + UT |
| 用相同引用/过滤/监听器退订 | 仅该监听器停止;其他监听器继续。 | IT + UT |
| 退订不存在的或不同的监听器 | 幂等 no-op。 | UT |
| 监听器抛异常 | 轮询与其他监听器继续。 | UT |
| HTTP 轮询使用发布 Client id | 只续订 Client 存活,不续订 Publisher 存活。 | IT |
| 轮询期间关闭 | 关闭后不投递回调。 | UT |
容量场景的实现佐证:shouldEnforceConfiguredLocalSubscriptionCapacityAndReuseSlot(L467-L498)读取nacos.agent.it.client.subscription.capacity(默认 3),用AiConstants.AI_AGENT_DISCOVERY_MAX_SUBSCRIPTIONS配置服务,断言超限时抛NacosApiException(CLIENT_OVER_THRESHOLD+AGENT_DISCOVERY_SUBSCRIPTION_OVER_LIMIT)。
九、完整端点发布(Complete Endpoint Publication)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| Agent 定义存在前注册 | 发布成功,但 Discover 保持 not-found 直到 Agent 可发现。 | IT |
| 注册一个或多个端点 | 完整 Batch 在匹配的 Runtime source 中可见。 | IT |
| 重复相同完整 Batch | 幂等成功且不产生重复端点。 | IT |
| 用变更的端点载荷替换 Batch | 新完整 Batch 替换旧端点与绑定值。 | IT + UT |
| 替换 Batch 时省略先前端点 | 被省略端点被移除。 | IT |
| 发布两个协议 | 每个(namespace, agent, protocol)状态独立,Discover 过滤正确。 | IT + UT |
| 两个 SDK 实例发布 | 贡献按自然键聚合,一个发布方注销不移除另一个。 | IT |
| 用一个完整 Batch 跨越本地发布水位 | 当操作前端点条目数低于水位时,整批被接纳并缓存,即使结果数超水位;不保留部分 Batch。 | IT + UT |
| 已处于或高于本地发布水位时增长 | 等大小或缩小的替换仍允许;新身份或增长的替换抛CLIENT_OVER_THRESHOLD,既不进入发布也不进入 gRPC 重放缓存。 | IT + UT |
| 服务端在其每 Client 水位拒绝发布增长 | SDK 暴露OVER_THRESHOLD,从所有重放/心跳状态移除被拒身份,在注销足够端点条目后重新接受。 | OpenAPI IT + Java SDK IT + UT |
| 缺失 range | 服务端收到有效 Batch,其 range 默认为精确 runtime 版本语义。 | IT + UT |
重复自然键、非法 URI/传输/版本/区间、空 Batch 或healthy输入 | SDK 本地拒绝 Batch 且不保留非法重放意图。 | IT + UT |
| 注册后调用方修改 Batch 或端点对象 | 存储的期望状态与重放载荷保持原始规范副本。 | UT |
| gRPC 端点 ability 不支持或未知 | SDK 本地失败,无 HTTP 或 legacy 回退。 | UT |
| HTTP 注册 | 发送稳定 Client id 与所需模块头;返回的存活区间调度心跳。 | IT + UT |
容量场景佐证:shouldEnforceConfiguredLocalPublicationCapacityAndReuseSlot(L500-L540)配置nacosAiAgentEndpointMaxPublications后断言超限拒绝码为CLIENT_OVER_THRESHOLD+AGENT_ENDPOINT_PUBLICATION_OVER_LIMIT;shouldSurfaceServerPublicationCapacityAndStopRejectedRedo(L542-L605)把本地水位设得高于服务端水位(nacos.agent.it.server.publication.capacity,默认 100),验证服务端拒绝后本地缓存与重放状态被清理。
十、部分与完整注销(Partial And Complete Deregistration)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| 从多端点 Batch 移除一个自然键 | SDK 发送一次仅含剩余部分的完整替换注册。 | IT + UT |
| 移除最后一个自然键 | SDK 发送一次整发布注销并移除本地期望状态。 | IT + UT |
| 注销未知本地发布或自然键 | 作为 no-op 成功,不发远程变更。 | IT + UT |
| 重复注销同一键 | 之后每次调用仍是 no-op。 | IT |
| 注销 URI 仅在路径、查询或主机拼写不同 | 匹配遵循规范主机、有效端口与传输的自然身份。 | UT |
| 注销试图包含 priority、weight、metadata 或 health | SDK 本地拒绝请求。 | UT |
| 注销一个协议而另一协议存在 | 另一协议保持注册并可发现。 | IT |
| 本地移除后替换注册失败 | 缩减后的期望 Batch 仍可用于后续重连重放。 | UT |
| 最后本地移除后整体注销失败 | 注销意图保留至完成或关闭。 | UT |
测试中的典型实现是shouldReplaceAndPartiallyDeregisterCompletePublications(L401-L465):它验证"路径不同但 authority 相同"的注销键仍能命中(replacePath辅助方法 L1673-L1676),以及 a2a/mcp 两协议的独立注册与独立移除。
十一、心跳、重试、重连与重放(Heartbeat, Retry, Reconnect, And Redo)
| 场景 | 预期结果 | 覆盖 |
|---|---|---|
| HTTP 心跳成功 | 使用同一 Client id,下次心跳遵循返回的有效区间。 | UT |
| 心跳之间的 HTTP 查询 | 不延长 Publisher 存活。 | IT |
HTTP 操作返回HTTP_CLIENT_NOT_FOUND (50404) | 该 HTTP SDK 实例的全部发布被标记未注册,并以完整 Batch 重放。 | Directed IT + UT |
| 50404 以外的瞬时 HTTP 心跳失败 | 保留期望发布状态,后续心跳可恢复且不创建新 Client id。 | UT |
| gRPC 断开后重连 | 每个期望发布被标记未注册并在新连接上重放一次。 | UT |
| 重连后 gRPC ability 变化 | 重新检查 ability;不支持或未知时阻止该连接上的重放。 | UT |
| 未知结果的写超时 | SDK 保留意图但不盲目切换传输。 | UT |
| 保留写之后收到参数或授权错误 | 重放停止、先前已确认发布被恢复、或首个非法意图被丢弃。 | UT |
| HTTP 与 gRPC 服务发布同一逻辑 Agent | 传输持有的重放记录与注销保持隔离。 | UT |
| 关闭时多个协议发布 | 每个完整发布在传输关闭前被尽力注销。 | UT |
十二、Directed 真实服务端生命周期(重启恢复)
该可选重连用例与普通 IT 使用同一生产发行版与公共AiService/AgentMaintainerServiceAPI。JUnit 进程从不启动或停止 Nacos:它写入 ready 标记,外部 harness 用同一数据目录停止并重启单机进程,测试仅在 harness 写入 restarted 标记后继续。
| 阶段 | 操作与断言 | 覆盖 |
|---|---|---|
| 初始服务端 | 创建并发布 legacy 兼容的 Version 1;为 Version 1、2 预注册 legacy exact-Version 端点进规范 Runtime Service;在同一 SDK 连接上注册协议无关的 gRPC 与 HTTP 端点 Batch;Search、legacy SERVICE 查询、latest/exact Discover、Runtime 绑定、轮询订阅一致且无发布覆盖。 | Directed IT |
| 服务端不可用 | 保持同一 gRPC/HTTP SDK 实例及其本地订阅/发布意图;两种传输都观察到连接不可用,但客户端进程存活且本地意图不删除。 | Directed IT + UT |
| 同一服务端重启 | gRPC SDK 重连并重放协议无关完整 Batch 及两个 legacy exact-Version 发布;HTTP 心跳收到HTTP_CLIENT_NOT_FOUND,保留同一外部 HTTP client id,创建全新服务端状态并重新注册完整 Batch;legacy 子发布者在新连接上重建,Runtime 绑定独立恢复,两传输轮询收敛。 | Directed IT + UT |
| 重连后的端点优先升级 | 在 legacy 端点已注册并恢复后创建并发布 Version 2;legacy SERVICE 查询、RAD Discover 与两个订阅立即解析该规范 Runtime Endpoint;后续协议无关的 gRPC/HTTP 发布者增加独立贡献而不替换它。 | Directed IT |
| 重连后的精确与 label 检查 | 精确 Version 1 仍可解析;exact/latest Version 2 经两传输一致;移动的自定义 label 解析到 Version 2。 | Directed IT |
| 清理 | 退订两个监听器,注销两个协议无关最终发布与 legacy Version 2 发布,验证精确 Runtime 池变空,删除 Agent,关闭客户端,重启后的服务端仍可供后续 IT 使用。 | Directed IT |
该场景有界等待两个标记及每个服务端/客户端收敛断言;若 harness 协调缺失,产生的是跳过或失败的目标运行,而不是沉睡的常规 CI 测试。对应测试方法为shouldRestoreGrpcAndHttpPublicationsAndPollingAfterRealServerRestart,通过系统属性nacos.agent.reconnect.enabled=true开启、nacos.agent.reconnect.control.dir指定标记目录(源码 L969-L982)。
十三、复合单机工作流(Compound Standalone Workflows)
| 工作流 | 交叉校验 | 覆盖 |
|---|---|---|
| Maintainer 创建并发布 Agent,随后 SDK Search 与 Discover | 管理端写入经两种客户端读操作可见。 | IT |
| legacy A2A SDK 发布 AgentCard 与 Version 1、2 的旧 exact-Version 端点,随后 Console Runtime Snapshot、RAD、legacy SERVICE、直接 Naming 读检查;Maintainer 发布 Version 2,随后省略/默认与显式 latest 发现比较池 | legacy 与协议无关表面共享规范 Agent 定义与 Runtime Service;重复发布不覆盖在线版本;预注册不创建定义;精确绑定独立存活;历史 Version 特定 Naming 服务在 Beta 阶段保持为空;省略选择聚合两个在线版本,显式 latest 仅含 Version 2。 | IT |
| SDK 预注册端点,Maintainer 创建/发布 Agent,SDK Discover | 预注册在无隐式定义创建下可见。 | IT |
| Maintainer 发布 Agent,SDK 注册/替换/部分注销/最终注销 | Discover 依序观察完整替换、剩余部分与空 Runtime source。 | IT |
| 两个 SDK 发布者注册同一端点,一个注销,再另一个注销 | 聚合可见性保持到最后贡献被移除。 | IT |
| 两个协议独立发布并过滤 | Search 协议目录与 Discover 协议过滤一致。 | IT |
| Agent 创建前启动订阅,随后定义与 Runtime Endpoint 出现 | 监听器仅在公共指纹变化时收到完整快照。 | IT |
| 移除订阅后定义或 Runtime Endpoint 变化 | 退订后无回调。 | IT |
| 同一工作流经 gRPC 与显式 HTTP 客户端 | 查询形态与发布语义传输等价。 | IT |
| Maintainer 发布三个 Agent,随后 gRPC 与 HTTP SDK 立即查询当前快照并分页/过滤收敛目录,期间一个 Agent 获得版本、接收 Runtime Endpoint、逐版本下线 | 两传输在无 readiness 错误下保持可用,收敛后返回相同稳定顺序与完整目录;类型化过滤组合一致;端点发布不改变 Search;生命周期投影收敛无陈旧版本。 | IT |
| public 与自定义命名空间工作流并行 | Search、Discover、订阅、发布从不跨命名空间。 | IT |
| Version 1 在线 + Version 2 端点预注册 + Version 2 发布 | Search、latest/exact/label Discover 与订阅在每个过渡点一致。 | IT |
| Version 1 在线 + 端点注册前 Version 2 发布 + 独立 Version 2 发布者 | 省略选择跨过渡保留 rollout 池,显式 latest 只观察 Version 2,两个轮询订阅收敛且无重复回调。 | IT |
| 活跃 gRPC/HTTP 发布与轮询订阅下真实单机重启 | 同一存活 SDK 进程恢复两个传输持有的完整 Batch,恢复两个轮询循环,随后观察到后续版本与两个端点。 | Directed IT |
十四、本阶段延后项(Deferred In This Phase)
| 项目 | 原因 |
|---|---|
服务端 Watch/Push、watchKey、Push ACK、gap recovery 与 Watch ability | 已批准的首版使用主动 Discover 轮询。 |
公共getAll/selectOneHealthy辅助 API 形态 | 设计已声明本地选择语义,但尚未规定稳定的 Java 类型与方法签名;不阻塞 Search、Discover、轮询或发布,作为记录而非本阶段发明。 |
| Agent 管理元数据变更通知 | AgentDiscoveryResult有意排除显示名、描述、标签、provider 等管理元数据。其轮询指纹仅包含解析版本、版本contentDigest与端点sourceRevision。未来订阅已发布 Agent 元数据强制更新的需求需要 Search/目录订阅或显式 RAD 契约扩展,不能由当前 Discover 订阅推断。 |
| 单帧级丢包与未知 gRPC 写结果歧义 | 由确定性单元故障注入覆盖;真实单节点进程重启单独覆盖;帧级故障注入不是稳定的单机 IT。 |
十五、如何运行与继续深入
- 场景矩阵文档:test/java-sdk-test/AGENT_DISCOVERY_SDK_IT_SCENARIOS.md
- 主测试类(18 个稳定工作流 + 1 个可选 Directed 重启工作流):AgentDiscoveryServiceJavaSdkITCase.java
- 公共 API 契约:AgentDiscoveryService.java、AiService.java(后者还扩展了
A2aService,提供releaseAgentCard、getAgentCard、subscribeAgentCard等 legacy 兼容面) - 工厂与传输模式:AiFactory.java、AgentTransportMode.java
- 容量与超时属性常量:AiConstants.java
- RAD 数据模型:api/src/main/java/com/alibaba/nacos/api/ai/model/rad(
AgentSearchRequest、AgentReference、AgentDiscoveryFilter、AgentDiscoveryResult、AgentEndpointRegistrationBatch、AgentEndpointDeregistrationBatch、AgentCatalogEntry、EndpointSet等) - 相关设计与协议规格:specs/en/ai/rad-protocol-spec.md、specs/en/ai/agent-api-spec.md、specs/en/ai/a2a-agent-spec.md
运行集成测试需先按 JAVA_SDK_IT_SCENARIOS.md 与 JAVA_SDK_IT_COVERAGE.md 的准备说明启动单机 Nacos(共享服务端 IT 不得停机);Directed 重启用例额外要求外部 harness 通过nacos.agent.reconnect.enabled与nacos.agent.reconnect.control.dir协调进程启停与标记文件。SDK 侧发布/订阅容量等行为化配置见nacosAiAgentEndpointMaxPublications(默认 100)与nacosAiAgentDiscoveryMaxSubscriptions(默认 300)。
结语
这份场景矩阵的价值在于把"协议无关 Agent 发现"这一复杂域拆解为可逐条验证的契约:从命名空间隔离、版本/标签语义、端点发布与注销、本地轮询订阅的去重与容量控制,到 gRPC/HTTP 传输等价与真实进程重启后的重连重放,每一行都对应明确的预期与验证层级。对二次开发者而言,它既是行为规格书,也是回归测试的活文档;对维护者而言,它界定了"首版不做 Watch/Push、不做管理元数据订阅、不做部分缓存"的清晰边界,为后续迭代(如批量 Wire Watch 的整批准入)预留了可扩展的契约位置。
【免费下载链接】nacosan easy-to-use dynamic service discovery, configuration and service management platform for building AI cloud native applications.项目地址: https://gitcode.com/GitHub_Trending/na/nacos
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考