服务发现不是 C# 自带能力,得靠第三方组件或自建逻辑
C# 本身不提供服务发现机制,.NET 运行时没有内置的注册中心、心跳检测或服务列表拉取功能。你得选一个支持 .NET 的服务发现方案,或者自己用 Consul / ETCD / Nacos 的 SDK 手动实现注册与发现逻辑。直接写
ServiceDiscovery.Register()是跑不通的——这函数根本不存在。 主流选择是集成
Steeltoe(专为 .NET 生态设计,支持 Eureka/Consul/Nacos)或直接用
Consul.NET+ 定时健康检查 若用 Kubernetes,可跳过客户端发现,改用 DNS + Headless Service,让
Dns.GetHostAddresses("orders-svc") 直接解析出所有 Pod IP
别在 Startup 中一次性读取服务列表就缓存到底——节点可能下线,必须配合 Watch或定期
GET /v1/health/service/orders拉取最新状态
用 Steeltoe 实现自动注册和负载均衡调用
Steeltoe 是目前最贴近 Spring Cloud Eureka 体验的 .NET 方案,它能在应用启动时自动向注册中心注册,并把
HttpClient包装成支持服务名寻址的客户端。 安装
Steeltoe.Discovery.ClientCore和对应实现包(如
Steeltoe.Discovery.ConsulCore) 在
Program.cs中调用
builder.Services.AddDiscoveryClient(),并配置
spring:cloud:discovery:service-name和注册中心地址 发起 HTTP 调用时,不用写
https://10.244.1.5:8080/api/order,而是用
https://orders-service/api/order,由
DiscoveryHttpMessageHandler解析服务名、选实例、重试失败节点 注意:默认轮询策略不带权重或故障剔除,高并发下需配合
LoadBalancerOptions启用响应时间加权或熔断
手动调用 Consul API 注册服务时容易漏掉健康检查
很多人只调一次
PUT /v1/agent/service/register就以为完事了,结果服务挂了注册中心还不知道,流量继续打过去。 必须同时注册
check字段,比如用 HTTP 健康端点:
"check": { "http": "http://localhost:5000/health", "interval": "10s" }
别用 TCP 检查("tcp": "localhost:5000"),.NET Kestrel 默认不暴露 TCP 健康端口,HTTP 更可控 注销不能只靠进程退出——要捕获
AppDomain.CurrentDomain.ProcessExit或
IHostApplicationLifetime.ApplicationStopping,主动发
PUT /v1/agent/service/deregister/{id}
本地开发时 Consul Agent 若没开 -dev模式,ACL 默认拒绝写操作,会报错
Unexpected response code: 403
K8s 环境下绕过客户端发现更简单,但要注意 DNS 缓存
在 Kubernetes 里,服务发现最轻量的方式就是放弃 SDK,靠集群 DNS + Headless Service,让
Service不带 ClusterIP,直接返回后端 Pod 的 A 记录。 定义 Service 时设
clusterIP: None,然后用
Dns.GetHostAddresses("orders-svc.default.svc.cluster.local") 获取全部 Pod IP
.NET 的 Dns类默认有缓存(TTL 由 DNS 响应决定),但 Windows 上有时会无视 TTL,建议加一层内存缓存 + 定时刷新(比如每 30 秒重查) 别直接用
HttpClient往 IP 列表轮询——没超时、没重试、没连接池复用;应该封装成
HttpMessageHandler,内部做健康标记和连接池隔离 如果 Pod 数量超过 100,DNS 响应可能被截断(EDNS0 不支持),这时得切回基于 Endpoints API 的 Watch 机制
服务发现的关键不在“怎么连上”,而在“怎么及时感知变化”。注册、心跳、注销、缓存更新、DNS TTL、SDK 的重试策略——每个环节断掉,都会让请求静默失败。别假设注册成功就一劳永逸。
