Documentation
¶
Overview ¶
Package client 提供 NNS(enjoydream Naming Service)治理核心: 服务发现、配置管理、实例订阅与注册的 provider 无关实现。
ServiceClient 不耦合任何注册中心 SDK:Registrar/Discoverer/Subscriber/ ConfigStore 经 With* 选项注入,未注入时处于降级态(mock/静态实例/缓存兜底)。 Nacos 默认构造由 nns 根门面(nns.New)承担——门面是 provider 选择点。 每次调用 New 返回独立实例(v0.15.2 起无包级单例)。
能力一览:
- 实例发现带多级降级链(短路静态 → mock → 新鲜缓存 → singleflight Discover → stale 缓存兜底 → 兜底静态实例),provider 抖动不断服务; 降级序可配置(WithFallbackOrder / WithStaticFallback),支持 「注册中心优先、静态地址兜底」的生产形态
- 多服务名注册:RegisterAs / DeregisterAs 让同一 SDK 连接注册多个端点名
- 配置热更新(WatchService / OnConfigChange),超时/重试/LB 策略 等运行时生效
- Close 为终结操作:注销监听与订阅、关闭默认路径创建的 SDK 客户端, 幂等;复用需重新 New
该包被 nns/kitex、nns/hertz、nns/endpoint 适配层消费,将完整降级链 桥接到各框架的服务发现接口。
Index ¶
- Variables
- type ClientOption
- func WithCacheExpiry(d time.Duration) ClientOption
- func WithConfigGroup(group string) ClientOption
- func WithConfigStore(s registry.ConfigStore) ClientOption
- func WithDiscoverer(d registry.Discoverer) ClientOption
- func WithFallbackOrder(order FallbackOrder) ClientOption
- func WithLocalServiceConfig(serviceName string, basic *metadata.ServiceBasic) ClientOption
- func WithMockMode(enabled bool) ClientOption
- func WithRegistrar(r registry.Registrar) ClientOption
- func WithStaticFallback(serviceName string, instances []registry.Instance) ClientOption
- func WithStaticInstances(serviceName string, instances []registry.Instance) ClientOption
- func WithSubscriber(s registry.Subscriber) ClientOption
- type ConfigChangeHandle
- type FallbackOrder
- type ServiceClient
- func (c *ServiceClient) AddStaticFallback(serviceName string, instances ...registry.Instance)
- func (c *ServiceClient) AddStaticInstance(serviceName string, instances ...registry.Instance)
- func (c *ServiceClient) BindServiceBasic(serviceName string, basic *metadata.ServiceBasic)
- func (c *ServiceClient) Close()
- func (c *ServiceClient) CloseWithContext(ctx context.Context) error
- func (c *ServiceClient) Deregister(ctx context.Context, port int, opts ...registry.RegisterOptionFunc) error
- func (c *ServiceClient) DeregisterAs(ctx context.Context, serviceName string, port int, ...) error
- func (c *ServiceClient) Discoverer() registry.Discoverer
- func (c *ServiceClient) GetAllInstances(ctx context.Context, serviceName string) ([]registry.Instance, error)
- func (c *ServiceClient) GetDepService() map[string]*metadata.ServiceBasic
- func (c *ServiceClient) GetInstances(ctx context.Context, serviceName string) ([]registry.Instance, error)
- func (c *ServiceClient) GetLocalColor() string
- func (c *ServiceClient) GetLocalRegion() string
- func (c *ServiceClient) GetLocalService() *metadata.ServiceBasic
- func (c *ServiceClient) GetRegistrar() registry.Registrardeprecated
- func (c *ServiceClient) GetServiceBasic(serviceName string) metadata.ServiceBasic
- func (c *ServiceClient) LoadDepService(ctx context.Context, serviceNames []string) error
- func (c *ServiceClient) LocalServiceInit(ctx context.Context, serviceName string) (*metadata.ServiceBasic, error)
- func (c *ServiceClient) MockMode() bool
- func (c *ServiceClient) OffConfigChange(handle *ConfigChangeHandle)
- func (c *ServiceClient) OnConfigChange(fn func(serviceName string)) *ConfigChangeHandle
- func (c *ServiceClient) Register(ctx context.Context, port int, opts ...registry.RegisterOptionFunc) error
- func (c *ServiceClient) RegisterAs(ctx context.Context, serviceName string, port int, ...) error
- func (c *ServiceClient) Registrar() registry.Registrar
- func (c *ServiceClient) RunWithLifecycle(app lifecycle.App, name string, port int, opts ...registry.RegisterOptionFunc) error
- func (c *ServiceClient) ServiceConfig(ctx context.Context, serviceName string) (metadata.ServiceBasic, error)
- func (c *ServiceClient) ServiceWithName(ctx context.Context, serviceName string) metadata.ServiceBasic
- func (c *ServiceClient) SetLocalServiceMeta(meta map[string]string)
- func (c *ServiceClient) SetStaticFallback(serviceName string, instances []registry.Instance)
- func (c *ServiceClient) SetStaticInstances(serviceName string, instances []registry.Instance)
- func (c *ServiceClient) SubscribeService(ctx context.Context, serviceName string, callback func([]registry.Instance)) error
- func (c *ServiceClient) UnsubscribeService(ctx context.Context, serviceName string) error
- func (c *ServiceClient) UnwatchService(ctx context.Context, serviceName string) error
- func (c *ServiceClient) WatchService(ctx context.Context, serviceName string)
Constants ¶
This section is empty.
Variables ¶
var ErrClientClosed = errors.New("nns: client closed")
ErrClientClosed 在 ServiceClient.Close() 之后调用订阅类方法时返回。 Close 是终结操作(v0.28.0 起),复用需重新 New。
ErrConfigStoreUnavailable 在需要真实配置拉取的入口(ServiceConfig)被调、 但未注入 ConfigStore(mock 模式之外)时返回(#77)。
ErrRegistrarUnavailable 历史上在 ServiceClient.Register 被调用但未配置 registrar 时返回。常见场景:未注入 registrar、Mock 模式未启用、provider 构造失败降级。
#66 起「没注册器」改为幂等 no-op success,本哨兵**不再被任何入口返回** (全仓零返回点),仅为兼容保留符号:消费方照旧编译不报错,但 errors.Is(err, ErrRegistrarUnavailable) 分支永不命中,不应再写依赖它的 错误分叉(判「没注册器」请直接看 MockMode()/Registrar())。
var ErrServiceNameRequired = errors.New("nns: service name required")
ErrServiceNameRequired 在显式指定服务名的注册/注销入口(RegisterAs / DeregisterAs)拿到空服务名时返回。
Functions ¶
This section is empty.
Types ¶
type ClientOption ¶
type ClientOption func(*ServiceClient)
ClientOption ServiceClient 的配置选项函数类型。
func WithCacheExpiry ¶
func WithCacheExpiry(d time.Duration) ClientOption
WithCacheExpiry 设置实例缓存的过期时间。
d <= 0(含零值缺省)时回退 defaultCacheExpiry(30s,与门面 nns.New 同缺省): 零值不表示「禁用过期检查」——容错层没有「永不过期」语义,注册中心故障时 的新鲜缓存兜底(Discover 链第 3 级)依赖正常的过期判定(nns#94)。
func WithConfigGroup ¶
func WithConfigGroup(group string) ClientOption
WithConfigGroup 设置配置读取的分组(默认 provider 配置里的 group 概念, 本包只把它当不透明字符串透传给 ConfigStore)。
func WithConfigStore ¶
func WithConfigStore(s registry.ConfigStore) ClientOption
WithConfigStore 注入自定义的配置存储(用于测试或替换 provider)。
func WithDiscoverer ¶
func WithDiscoverer(d registry.Discoverer) ClientOption
WithDiscoverer 注入自定义的服务发现器(用于测试或替换 provider)。
func WithFallbackOrder ¶ added in v0.5.0
func WithFallbackOrder(order FallbackOrder) ClientOption
WithFallbackOrder 设置全局降级序:静态实例与注册中心谁优先。
缺省为 FallbackStaticFirst(静态实例短路注册中心,与既有行为一致)。 设为 FallbackRegistryFirst 后,全部服务的静态实例降级为「兜底」: 注册中心路径失败或返回空时才参与取值。
单服务可用 WithStaticFallback 单独指定兜底语义,不受全局序影响。
func WithLocalServiceConfig ¶
func WithLocalServiceConfig(serviceName string, basic *metadata.ServiceBasic) ClientOption
WithLocalServiceConfig 配置本地服务配置(不从 Nacos 获取) 用于本地开发或测试环境,直接指定服务的超时、重试等配置
func WithMockMode ¶
func WithMockMode(enabled bool) ClientOption
WithMockMode 设置是否启用 Mock 模式,Mock 模式下不连接 Nacos。
func WithRegistrar ¶
func WithRegistrar(r registry.Registrar) ClientOption
WithRegistrar 注入自定义的服务注册器(用于测试或替换 provider)。
func WithStaticFallback ¶ added in v0.5.0
func WithStaticFallback(serviceName string, instances []registry.Instance) ClientOption
WithStaticFallback 配置指定服务的**兜底**静态实例。
与 WithStaticInstances 的区别只在参与时机:
- WithStaticInstances:短路静态——配了就直接返回,注册中心不被查询;
- WithStaticFallback:兜底静态——只有注册中心路径失败或返回空时才返回它。
典型用法是生产形态的「注册中心优先、静态地址兜底」:静态地址 (如 k8s Service DNS)与注册中心同时配置,注册中心有健康实例时以 注册中心为准(静态地址在实例换 IP 后不会跟着变),注册中心冷启动或 故障时才用静态地址兜底。
同一服务上重复配置以最后一次调用为准:WithStaticFallback 与 WithStaticInstances 写同一份静态实例表,前者把该服务标记为兜底语义。
权重与注册侧同口径:Weight<=0(漏配或显式 0)回填默认权重 10, 显式正权重原样保留——见 normalizeStaticInstances。
func WithStaticInstances ¶
func WithStaticInstances(serviceName string, instances []registry.Instance) ClientOption
WithStaticInstances 配置指定服务的静态实例列表,用于本地开发或特殊场景。
默认是**短路**语义:配了该服务的静态实例后,GetInstances 直接返回它们, 不再查询注册中心。需要「注册中心优先、静态地址兜底」的生产形态请用 WithStaticFallback(或全局 WithFallbackOrder(FallbackRegistryFirst))。
同一服务上与 WithStaticFallback 重复配置时以最后一次调用为准——本选项把 该服务恢复为短路语义。
权重与注册侧同口径:Weight<=0(漏配或显式 0)回填默认权重 10, 显式正权重原样保留——见 normalizeStaticInstances。
func WithSubscriber ¶
func WithSubscriber(s registry.Subscriber) ClientOption
WithSubscriber 注入自定义的服务订阅器(用于测试或替换 provider)。
type ConfigChangeHandle ¶ added in v0.8.0
type ConfigChangeHandle struct {
// contains filtered or unexported fields
}
ConfigChangeHandle 是 OnConfigChange 注册配置变更回调的注销句柄。
Go 函数值不可比较,无法按函数身份摘除——注册条目以指针包装存储, 调用方保存 OnConfigChange 返回的句柄、传给 OffConfigChange 即可精确 注销该次注册(与 instanceCallback 的指针包装同一口径)。零值句柄与 重复注销均为 no-op。
典型生命周期:组件(如 endpoint factory)构造时注册、Close 时注销—— 否则长生命周期的 ServiceClient 会经回调闭包持续持有已废弃的组件。
type FallbackOrder ¶ added in v0.5.0
type FallbackOrder int
FallbackOrder 决定静态实例与注册中心的取值优先序。
静态实例有两种角色,差别只在「什么时候参与取值」:
- 短路静态(WithStaticInstances,FallbackStaticFirst 下的默认):为服务 配了静态实例就直接返回,不再查询注册中心。适合本地开发、测试。
- 兜底静态(WithStaticFallback,或全局 FallbackRegistryFirst 下的 静态实例):先查注册中心,注册中心路径失败或返回空时才用静态实例 兜底。适合生产形态——静态地址(如 k8s Service DNS)只是最后一层, 注册中心有健康实例时以注册中心为准。
const ( // FallbackStaticFirst 静态实例优先(默认):配了静态实例就短路注册中心。 // // 保持既有行为不变:已按短路语义配置静态实例的调用方不受影响。 FallbackStaticFirst FallbackOrder = iota // FallbackRegistryFirst 注册中心优先:先查注册中心,注册中心路径失败 // 或返回空时才用静态实例兜底。 // // 适用于「注册中心优先、静态地址兜底」的生产形态:注册中心有健康 // 实例时不再被静态地址遮蔽(静态地址在实例换 IP 后不会跟着变), // 而注册中心冷启动、故障且缓存为空时仍有兜底可用。 FallbackRegistryFirst )
func (FallbackOrder) String ¶ added in v0.5.0
func (o FallbackOrder) String() string
String 返回降级序的可读名称。
type ServiceClient ¶
type ServiceClient struct {
// contains filtered or unexported fields
}
ServiceClient NNS 服务核心客户端,管理服务发现、配置、实例缓存和订阅。
依赖 registry 包定义的 4 个接口(Registrar/Discoverer/Subscriber/ConfigStore), 不直接耦合任何具体注册中心 SDK:provider 经 With* 选项注入,未注入即 降级态(mock/静态实例/缓存兜底);Nacos 默认构造由 nns 根门面承担。
func New ¶
func New(opts ...ClientOption) *ServiceClient
New 创建一个独立的 ServiceClient 实例(provider 无关)。
每次调用都返回全新实例,互不影响。本包不绑定任何注册中心 provider: 通过 WithRegistrar/WithDiscoverer/WithSubscriber/WithConfigStore 注入 实现(如 mock 或替代 provider)。未注入任何接口且未开 Mock 时保持 降级态(namingAvailable()==false,走缓存/静态实例路径)——Nacos 默认 构造由 nns 门面负责(门面是 provider 选择点),见 nns.New。
func (*ServiceClient) AddStaticFallback ¶ added in v0.6.0
func (c *ServiceClient) AddStaticFallback(serviceName string, instances ...registry.Instance)
AddStaticFallback 追加**兜底**静态实例(与 WithStaticFallback 一致的语义)。
与 AddStaticInstance 的区别只在参与时机:本入口把该服务标记为兜底语义, **不改变发现路径**——注册中心仍是第一数据源,只有注册中心路径失败或返回 空时静态实例才参与取值。运行期热加备用地址(管理面/脚本)用它,不会切断 注册中心发现(#128)。
权重与注册侧同口径:Weight<=0 回填默认权重 10(见 normalizeStaticInstances)。
func (*ServiceClient) AddStaticInstance ¶
func (c *ServiceClient) AddStaticInstance(serviceName string, instances ...registry.Instance)
AddStaticInstance 添加静态服务实例(**短路**语义,与 WithStaticInstances 一致)。
⚠️ 会改变发现路径(#128):FallbackStaticFirst(缺省)下,此前没有静态 配置的服务被写入后即**短路注册中心**——实例上下线、权重调整、健康摘除 不再反映到 GetInstances/GetAllInstances。运行期热加「备用地址」请改用 AddStaticFallback(兜底语义,注册中心发现保持不变);确要切到短路时本 函数会打 Warn 点明后果。已有语义标记跟随(fallbackOnly 不被改写): 兜底语义的服务追加实例仍是兜底。
权重与注册侧同口径:Weight<=0 回填默认权重 10(见 normalizeStaticInstances)。
func (*ServiceClient) BindServiceBasic ¶
func (c *ServiceClient) BindServiceBasic(serviceName string, basic *metadata.ServiceBasic)
BindServiceBasic 解析服务全名并设置服务基本配置的默认值。
只对**未配置**(<=0)的超时回填默认值(ConnTimeout 1000ms / ReadTimeout 5000ms / WriteTimeout 5000ms);显式配置的取值原样保留——1–49ms 的低延迟 超时不再被改写成默认值(#127:此前 <50 一律静默钳成 1000/5000ms,20ms 的配置被吞 50 倍且零日志),仅打 Warn 防呆、不改写。
Retry:负值钳 0(负重试无意义;这是仅存的改写,必留日志);>15 原样保留 并 Warn——不再静默钳 15(#127)。
本函数是配置消费的必经点(LocalServiceInit / LoadDepService / ServiceConfig / ServiceWithName / WatchService 热更回调),口径统一在 这里收口:配置中心原文与生效值一致。
func (*ServiceClient) Close ¶
func (c *ServiceClient) Close()
Close 释放 ServiceClient 持有的资源(幂等)。 停止所有配置监听、注销实例订阅、清空回调状态,最后关闭默认路径创建的 Nacos SDK 客户端(各持 gRPC 长连接 + 后台 goroutine)。 保证 Close 后实例可安全重建(重新 WatchService/OnConfigChange 不残留旧状态)。
func (*ServiceClient) CloseWithContext ¶ added in v0.5.1
func (c *ServiceClient) CloseWithContext(ctx context.Context) error
CloseWithContext 关闭并返回错误(ctx 有界)。
与 Close 的差别只有两点:
- **ctx 有界**:Close 内部原先用 context.Background() 调 SDK 的 Unsubscribe / StopListenConfig ⇒ SDK 不响应时 Close **永久挂住** (调用方无从得知卡在哪一步)。这里把 ctx 透传给调用方;Close 走 默认 5s 上限(见 closeDefaultTimeout)。
- **错误不吞**:注销订阅 / 停止配置监听的失败原样返回(Close 入口仍 只记日志——它没有返回值可给)。
幂等:与 Close 共享同一个 sync.Once,重复调用只有第一次真正执行。
func (*ServiceClient) Deregister ¶
func (c *ServiceClient) Deregister(ctx context.Context, port int, opts ...registry.RegisterOptionFunc) error
Deregister 从注册中心注销当前服务实例。
实例身份优先取 Register 成功时缓存的快照(#75):注册中心按 ip+port+service+group(+cluster)匹配实例,若注销时按当下环境重新推导—— 注册与注销之间 GetPodIP 结果变化(网卡枚举顺序变化/POD_IP 变更)、或 注册时传了 WithGroup/WithCluster 而注销时漏传——就会匹配不到实例, 注销**静默失效**、实例残留在注册中心继续接流量。
没有缓存(本 client 未注册过,如经 Registrar() 逃生口自行注册的调用方) 时退回旧行为:按当下环境与 opts 现算实例身份——此时调用方必须保证 IP/Group/Cluster 与注册时完全一致。注销成功后缓存清除,再次 Deregister 走现算路径。
func (*ServiceClient) DeregisterAs ¶ added in v0.5.0
func (c *ServiceClient) DeregisterAs(ctx context.Context, serviceName string, port int, opts ...registry.RegisterOptionFunc) error
DeregisterAs 从注册中心注销指定服务名下的当前服务实例。
与 RegisterAs 配套:实例身份优先取 RegisterAs 成功时缓存的快照(#75), IP/Group/Cluster 不再依赖注销时的环境与 opts;无缓存时按当下环境现算, 调用方须自行保证与注册时一致。
未配置 registrar 时返回 nil(与 Deregister 一致:注销是幂等收尾, 没有 registrar 说明根本没注册过);服务名为空时返回 ErrServiceNameRequired (参数校验先于环境检查)。
func (*ServiceClient) Discoverer ¶ added in v0.5.0
func (c *ServiceClient) Discoverer() registry.Discoverer
Discoverer 返回底层注入的发现器;未注入时返回 nil。
逃生口:与 Registrar 配套,让调用方直接驱动发现路径 (如按需携带分组/集群参数拉取实例),连接与生命周期仍归门面。
func (*ServiceClient) GetAllInstances ¶
func (c *ServiceClient) GetAllInstances(ctx context.Context, serviceName string) ([]registry.Instance, error)
GetAllInstances 获取指定服务的全部实例列表(包括不健康的实例)。
与 GetInstances 同一套容错/静态语义(#129——此前两个入口整体分叉:短路 服务照样打注册中心、失败无兜底、空结果不兜底、降级分支把兜底服务当短路 返回,同一配置两个入口给出不同实例面),差别只有两点:
- 取「全部实例」走 DiscoverAll(含不健康实例);
- 结果**不写入**实例缓存(实例缓存只存健康视图,供 GetInstances 路由)。
优先序(与 GetInstances 逐级对齐):
- 短路语义的静态配置(同 GetInstances 第 1 级)
- Mock 模式(同 GetInstances 第 2 级)
- 无 Discoverer:新鲜缓存 → 兜底静态 → 报错(同 GetInstances 第 3/4 级)
- DiscoverAll(singleflight 保护,"all:" 前缀与 GetInstances 的 flight 分开;失败降级 stale 缓存 → 兜底静态 → 报错,同第 5/6 级)
- 空结果兜底静态(同 GetInstances 第 6 级)
返回值契约与 GetInstances 一致:空列表 + nil error 表示「当前无可用实例, 由调用方决定回落」;返回的也是**已发布只读快照**(见 GetInstances 的 快照契约,#149)——DiscoverAll 的结果在 singleflight 出口做一次拷贝与 provider 内存解耦,并发等待方共享同一只读快照。
func (*ServiceClient) GetDepService ¶
func (c *ServiceClient) GetDepService() map[string]*metadata.ServiceBasic
GetDepService 返回依赖服务配置的快照(并发安全)。 注意是结构体浅拷贝:嵌套的指针/map 字段(如 TimeoutRetryCfg)与内部状态共享, 调用方不得修改;并发安全由 WatchService 对字段的整体替换保证。 建议使用此方法而非直接访问内部 depService 字段。
func (*ServiceClient) GetInstances ¶
func (c *ServiceClient) GetInstances(ctx context.Context, serviceName string) ([]registry.Instance, error)
GetInstances 获取服务实例,支持本地缓存和静态配置。
解析优先级(从高到低):
- 静态配置(本地开发、测试)——**仅在短路语义下**(见 WithStaticInstances 与 WithStaticFallback 的区别);兜底语义下静态实例不在此步参与
- Mock 模式(静态或缓存)
- 新鲜缓存(由 Subscribe 推送或上一次 Discover 填充,在 cacheExpiry 内)
- 注册中心 Discover(singleflight 保护,避免缓存击穿)
- Discover 失败时降级使用缓存(含已过期条目)
- 兜底静态实例(WithStaticFallback / FallbackRegistryFirst)——仅在 注册中心路径拿不到实例时参与
第 6 步让「注册中心优先、静态地址兜底」的生产形态成立:注册中心有健康 实例时以注册中心为准(静态地址在实例换 IP 后不会跟着变),注册中心冷启动 或故障时仍有静态地址兜底。
返回值契约:**空列表 + nil error** 表示「当前无可用实例,由调用方决定 回落」(Mock 模式下未配静态实例的名字即走这条路径)。调用方只判 err 不判 len 会把请求路由到空——httpx 的 NNSResolver 判了 len 并回落 baseURL, 本包的契约以此为准。
快照契约(#149,原「每请求深拷贝」契约收紧) ¶
返回的是内部**已发布只读快照**的引用:本包对实例数据只做「发布时一次 深拷贝 + 整体替换」(RCU),绝不原地改写任何已发布的快照,也绝不外泄 provider/调用方拥有的可变内存。因此:
- 调用方**不得修改**返回的实例(含 Metadata map)——改动对本进程后续 读者可见(热路径零拷贝的代价);需要私有可能改的副本请自行克隆;
- 持有旧快照的调用方不受后续推送/发现更新影响(更新整体替换,旧快照 内容恒定);
- 缓存命中热路径零分配(此前每请求深拷贝实例列表 + 每实例一个 Metadata map,nns-endpoint 每请求调用的场景是可测量的稳态分配)。
func (*ServiceClient) GetLocalColor ¶
func (c *ServiceClient) GetLocalColor() string
GetLocalColor 获取本地服务的灰度标识颜色。 颜色来自本地服务元数据,可通过 SetLocalServiceMeta 设置。
func (*ServiceClient) GetLocalRegion ¶
func (c *ServiceClient) GetLocalRegion() string
GetLocalRegion 获取本地服务所在的区域信息。
func (*ServiceClient) GetLocalService ¶
func (c *ServiceClient) GetLocalService() *metadata.ServiceBasic
GetLocalService 获取本地服务的基本配置信息。
func (*ServiceClient) GetRegistrar
deprecated
func (c *ServiceClient) GetRegistrar() registry.Registrar
GetRegistrar 返回内部的 registrar 接口(供 nns/kitex 适配器使用)。
Deprecated: 与 Registrar() 完全等价(#146 双访问器收敛)。新代码请用 Registrar();kitex/hertz 适配层迁移完成前本转发保留,之后随 major 删除。
func (*ServiceClient) GetServiceBasic ¶
func (c *ServiceClient) GetServiceBasic(serviceName string) metadata.ServiceBasic
GetServiceBasic 返回指定服务的内存配置(不发起网络请求)。
优先从已加载的 DepService / LocalService 中读取(由 Init 中的 WatchService 保证热更新), 未加载时返回带默认值的 ServiceBasic。供 endpoint 在每次调用时快速读取超时等配置。
func (*ServiceClient) LoadDepService ¶
func (c *ServiceClient) LoadDepService(ctx context.Context, serviceNames []string) error
LoadDepService 加载依赖服务的配置信息。 采用尽力而为策略:即使某个服务加载失败,也会继续加载其余服务。 所有遇到的错误将通过 errors.Join 聚合后返回。
func (*ServiceClient) LocalServiceInit ¶
func (c *ServiceClient) LocalServiceInit(ctx context.Context, serviceName string) (*metadata.ServiceBasic, error)
LocalServiceInit 初始化本地服务,从 Nacos 配置中心拉取服务配置。
func (*ServiceClient) MockMode ¶
func (c *ServiceClient) MockMode() bool
MockMode 返回是否处于 Mock 模式。供 transport 适配器区分 "mock 静默跳过"与"降级(registrar 不可用)"两种 registrar==nil 场景。
func (*ServiceClient) OffConfigChange ¶ added in v0.8.0
func (c *ServiceClient) OffConfigChange(handle *ConfigChangeHandle)
OffConfigChange 按 OnConfigChange 返回的句柄注销对应回调。 幂等:nil 句柄、重复注销或句柄已随 Close 清空均为 no-op。
func (*ServiceClient) OnConfigChange ¶
func (c *ServiceClient) OnConfigChange(fn func(serviceName string)) *ConfigChangeHandle
OnConfigChange 注册配置变更回调,返回注销句柄。 当 WatchService 检测到服务配置变更时,所有已注册的回调会被触发; 回调列表在事件分发时现读,注册顺序不影响事件送达。 典型用途:endpoint factory 注册 balancer 缓存重置。
保存返回的句柄并在组件 Close 时传给 OffConfigChange 注销——长生命周期的 ServiceClient 会经回调闭包持有注册方,注册方废弃后不注销则无法回收, 配置事件也会继续打在死组件上(oekit/nns-endpoint#135)。
func (*ServiceClient) Register ¶
func (c *ServiceClient) Register(ctx context.Context, port int, opts ...registry.RegisterOptionFunc) error
Register 向注册中心注册当前服务实例。
服务名取 LocalServiceInit 记录的自身服务名(须先 Init),使用 GetPodIP 探测本机 IP。opts 控制权重、元数据等注册选项。
注册成功后实例身份(IP/Group/Cluster 等)会被缓存(#75):配套的 Deregister 优先用该快照注销,注册与注销之间 GetPodIP 结果变化、或注销时 漏传 WithGroup/WithCluster,都不会再让注销静默失效。
未配置 registrar(mock/未启用注册发现)时是幂等 no-op success(#66), 不返回错误——「本来就没注册」不是故障;no-op 判定先于服务名检查 (#142 收敛前后的行为保持:无 registrar 时即使未 Init 也返回 nil)。
func (*ServiceClient) RegisterAs ¶ added in v0.5.0
func (c *ServiceClient) RegisterAs(ctx context.Context, serviceName string, port int, opts ...registry.RegisterOptionFunc) error
RegisterAs 向注册中心注册当前服务实例到**指定服务名**下。
与 Register 的区别只有一处:服务名由调用方显式给出,而不是取 LocalServiceInit 记录的自身服务名。因此 RegisterAs **不要求先 Init**。
一个服务对外暴露多个端点(HTTP + RPC 双协议、分别注册在短名与完整 RPC 服务名下)时,用同一个 SDK 连接把每个名字各注册一次即可——不必 为每个名字各开一个门面实例(那会翻倍 Nacos gRPC 长连接与后台 goroutine)。
端口各自传入(同一进程的两个端点端口不同)。IP 与 Register 一致,取 GetPodIP 探测的本机 IP。opts 控制权重、元数据等注册选项。
配套的注销入口是 DeregisterAs:注册了几个名字就要注销几个名字, 否则注册中心里会留下无人回收的实例。
注册成功后实例身份会被缓存(#75),DeregisterAs 优先用快照注销。
未配置 registrar(mock/未启用注册发现)时是幂等 no-op success(#66), 不返回错误——与 Register 一致,注销对称性不再依赖调用方纪律。 ErrRegistrarUnavailable 自 #66 起**不再被任何入口返回**(仅兼容保留符号, 见其声明),任何 errors.Is(err, ErrRegistrarUnavailable) 分支都不会命中。 服务名为空时返回 ErrServiceNameRequired(参数校验先于环境检查——空服务名 是调用方的错,报「registrar 不可用」会把它掩盖掉)。
func (*ServiceClient) Registrar ¶ added in v0.5.0
func (c *ServiceClient) Registrar() registry.Registrar
Registrar 返回底层注入的注册器;未注入时返回 nil。
逃生口:把「注册什么」交还给调用方(如按调用注入不同的实例字段), 而 SDK 连接与生命周期仍由门面持有与释放。与 Discoverer 配套。
func (*ServiceClient) RunWithLifecycle ¶
func (c *ServiceClient) RunWithLifecycle(app lifecycle.App, name string, port int, opts ...registry.RegisterOptionFunc) error
RunWithLifecycle 向注册中心注册当前服务实例,并通过 lifecycle.App 注册 Deregister 作为优雅关闭时的 closer,业务无需手写 Register + Deregister 对称调用。
调用顺序契约(#76):必须在 server 资源注册**之后**调用 ¶
lifecycle.App 的 closer 按注册**逆序**执行。本方法注册的 deregister closer 要在关停时**先于** server.Shutdown 执行(先摘流量再停监听,对齐 bootstrap/nacos 组件与 go-kratos Stop 语义),因此 deregister closer 必须 **晚于** server 的 closer 注册——即先启动 server(hertz/kitex RunServer 内部经 app.AddResource 注册 server.Shutdown),再调本方法:
b, _ := bootstrap.New(bootstrap.WithServiceName("svc")) // + nacos/obs WithXxx 按需注入
b.Run(func(app *bootstrap.App) error {
nnsc := nns.New(nacosBootstrap, opts...) // 门面负责 provider 默认构造
h := server.New(server.WithHostPorts(":8080"))
hertz.RunServer(app, "hertz", h,
hertz.WithOnReady(func(ctx context.Context) { app.Ready() }))
// ⚠️ 必须在 RunServer 之后:server closer 先注册 ⇒ 关停逆序执行时
// nns-deregister 先于 server.Shutdown(先摘流量,再停监听)。
if err := nnsc.RunWithLifecycle(app, "my-svc", 8080); err != nil {
return err
}
<-app.Context().Done() // 阻塞直到优雅关闭
return nil
})
反例(旧示例的写法,勿用):先 RunWithLifecycle 再启动 server——关停时 server 先停监听、实例才从注册中心摘除,窗口内 LB 仍把流量路由到已死的 实例。需要「注册严格等到 server 就绪」的语义时,用 bootstrap/nacos 的 WithNNS/WithRequiredNNS(注册延迟到 app.Ready() 之后)。
⚠️ 行为:
- Register 失败:返回 error,business 决定是否继续(通常 return err 触发整体退出)
- Register 成功 + lifecycle 关闭:Deregister 在 bootstrap 关闭序列里被调, 走 lifecycle.App.AddResource 的逆序释放约定
- opts 同时传给 Register 和 Deregister,要求 opts 必须是幂等的(典型场景 只传 weight / metadata / group,业务不要传 random token 之类)
官方依据:ekit lifecycle 契约(lifecycle.App.AddResource)+ Kitex 官方 graceful_shutdown 章节"注册中心注销"步骤
func (*ServiceClient) ServiceConfig ¶ added in v0.5.10
func (c *ServiceClient) ServiceConfig(ctx context.Context, serviceName string) (metadata.ServiceBasic, error)
ServiceConfig 拉取指定服务的配置并返回**真实错误**(#77)。
与 ServiceWithName 的差别只在错误面:
- ServiceWithName 把配置拉取失败/JSON 解析失败静默降级为默认超时值 (软依赖消费方要「有默认值就能跑」);
- 本方法把错误原样上抛——需要区分「拉到了配置」与「拿到的是兜底默认值」 的调用方(如强依赖配置的启动校验)用它取到真实结果。
错误语义:
- GetConfig 失败(配置中心不可达等):返回包装后的原始错误;
- 配置内容 JSON 损坏:返回解析错误(配置写坏应显式暴露,而非静默 降级默认值);
- 未注入 ConfigStore(mock 之外):返回 ErrConfigStoreUnavailable—— 此前该场景在 GetService 门面上表现为「恒成功 + 默认超时值」;
- 配置未发布(content 为空)不算错误:返回默认值 + nil,与 ServiceWithName 一致;
- Mock 模式:取 localServiceConfig,未配置则默认值,不报错。
func (*ServiceClient) ServiceWithName ¶
func (c *ServiceClient) ServiceWithName(ctx context.Context, serviceName string) metadata.ServiceBasic
ServiceWithName 根据服务全名获取服务基本配置。
软依赖语义:配置拉取失败/JSON 解析失败时静默降级——日志 Warn 后返回 默认超时值(硬失败面见 ServiceConfig)。主干与 ServiceConfig 共享 fetchServiceConfig(#144)。
func (*ServiceClient) SetLocalServiceMeta ¶
func (c *ServiceClient) SetLocalServiceMeta(meta map[string]string)
SetLocalServiceMeta 设置本地服务元数据(如 color/region 灰度标识)。 并发安全;传入 nil 等效于清空。典型来源是启动时的环境变量或平台注入。
func (*ServiceClient) SetStaticFallback ¶ added in v0.6.0
func (c *ServiceClient) SetStaticFallback(serviceName string, instances []registry.Instance)
SetStaticFallback 设置**兜底**静态实例(覆盖,与 WithStaticFallback 一致 的语义)。
与 SetStaticInstances 的区别只在参与时机:本入口把该服务标记为兜底语义, **不改变发现路径**(#128)。权重口径与 AddStaticFallback 一致。
func (*ServiceClient) SetStaticInstances ¶
func (c *ServiceClient) SetStaticInstances(serviceName string, instances []registry.Instance)
SetStaticInstances 设置静态服务实例(覆盖,**短路**语义,与 WithStaticInstances 一致)。
⚠️ 同 AddStaticInstance:会把该服务切到静态短路(切路径时 Warn); 热加备用地址请用 SetStaticFallback。已有语义标记跟随(fallbackOnly 不被改写)。
权重与注册侧同口径:Weight<=0 回填默认权重 10(见 normalizeStaticInstances)。
func (*ServiceClient) SubscribeService ¶
func (c *ServiceClient) SubscribeService(ctx context.Context, serviceName string, callback func([]registry.Instance)) error
SubscribeService 订阅指定服务的实例变更,当实例列表变化时回调 callback。
SDK 监听按服务去重:同一服务多次订阅只向 provider 注册一次监听, 推送时扇出到全部本地回调(否则 N 次订阅会导致每次推送触发 N² 次回调)。 去重路径(他人已注册监听)下,新回调会立即收到一次当前静态/缓存实例的 初始回调——SDK 推送是事件驱动的,若 provider 首推已过,否则新订阅方会 "看似订阅成功却迟迟无回调"。Close 后调用返回 ErrClientClosed。
回调收到的 []registry.Instance 为**只读快照**(#149):推送路径是与 provider 内存解耦的私有副本,初始回调是内部已发布快照的引用;两者均 不得修改(快照契约见 GetInstances godoc)。
func (*ServiceClient) UnsubscribeService ¶
func (c *ServiceClient) UnsubscribeService(ctx context.Context, serviceName string) error
UnsubscribeService 取消订阅指定服务的实例变更。 本地回调列表同步清除,避免再次 Subscribe 后旧回调重复触发。 注意:取消针对整个服务生效(所有调用方的回调一并清除), SDK 监听同时注销,下次 Subscribe 会重新注册。
并发纪律(#147):「清本地状态 + 标记退订中」与「provider 退订」之间 不是原子的。退订期间(unsubscribing 标记在位)并发 SubscribeService 会 像 etc 等待在途注册一样等待退订落地后再注册新监听——否则本次 provider 退订会把窗口里建立的新监听一并拆除,而本地已视为已订阅 ⇒ 永不重注册, 实例变更推送全部丢失。并发多次 Unsubscribe 收敛:后来者等待首次退订 结束后按「未订阅」状态重入(对 provider 幂等)。
func (*ServiceClient) UnwatchService ¶ added in v0.7.0
func (c *ServiceClient) UnwatchService(ctx context.Context, serviceName string) error
UnwatchService 取消指定服务的配置监听(WatchService 的对称退订入口, #148)。
此前配置监听只能随 Close 全量终结:动态增删依赖服务的场景(网关按配置 热加载 dep 列表)里,下线服务的监听与 goroutine 只能积累到进程 Close。 本入口摘除去重标记并 StopListenConfig(ConfigStore 按 dataId 取消监听, Close 路径同款机制),单个服务的配置监听可独立回收。
未监听过的服务是幂等 no-op(返回 nil);Mock 模式或未注入 ConfigStore 时同样 no-op(与 WatchService 的 no-op 对称)。取消后可重新 WatchService。 监听注册是异步的:WatchService 刚返回时后台注册可能尚未完成,此刻调用 本方法对「已在途的注册」由 StopListenConfig 的 dataId 粒度取消兜底。
func (*ServiceClient) WatchService ¶
func (c *ServiceClient) WatchService(ctx context.Context, serviceName string)
WatchService 监听指定服务的配置变更。 内部做去重:同一 serviceName 只注册一次监听。