服务发现
更新: 2026-10-16 15:20:09 字数: 0 字 时长: 0 分钟
服务发现解决的是「服务之间怎么找到对方」。有了它,前端不需要硬编码 http://localhost:5001,只要引用服务名 catalog,Aspire 会在运行时解析出真实地址。
工作原理
当您用 WithReference 连接两个服务时,Aspire 自动完成四件事:
- AppHost 声明资源并分配端点。
- 通过环境变量把配置注入到每个服务中。
- 您的代码使用逻辑名称,例如
https+http://catalog。 - Aspire 的解析器在运行时把名称翻译成真实地址。
关键点:代码里始终使用逻辑服务名,由配置驱动的端点解析器在运行时完成翻译。
var builder = DistributedApplication.CreateBuilder(args);
var catalog = builder.AddProject<Projects.CatalogService>("catalog");
var basket = builder.AddProject<Projects.BasketService>("basket");
var frontend = builder.AddProject<Projects.MyFrontend>("frontend")
.WithReference(basket)
.WithReference(catalog);在上面的例子里,frontend 引用了 catalog 与 basket。这两次 WithReference 做了两件事:
- 注入配置:把
catalog与basket的服务发现信息注入frontend。 - 启用解析:允许
frontend在HttpClient中使用https+http://catalog这样的 URI。
不调用 WithReference 就发现不了
如果某个服务没有显式 WithReference 另一个服务,它就不会获得对方的服务发现配置。这是刻意设计——服务只知道自己声明的依赖。
为什么这很重要
服务发现消除了「在我机器上能跑」的问题。同一份代码在本地(catalog 可能是 localhost:5001)与生产环境(可能是 catalog.internal.cloudapp.net:443)都无需修改。
在 .NET 中使用
ServiceDefaults 已经为所有 HttpClient 启用服务发现与弹性策略。声明一个强类型客户端即可:
builder.Services.AddHttpClient<CatalogServiceClient>(
static client => client.BaseAddress = new("https+http://catalog"));https+http:// 的含义是:优先使用 HTTPS,如果对方只提供 HTTP 则自动回退。这是本地开发中最常用的写法。
命名端点
有些服务会暴露多个端点(例如一个业务端点加一个管理端点)。此时在 URI 的 host 部分写端点名即可,格式为 scheme://_endpointName.serviceName:
// 默认端点
builder.Services.AddHttpClient<BasketServiceClient>(
static client => client.BaseAddress = new("https+http://basket"));
// 名为 dashboard 的端点
builder.Services.AddHttpClient<BasketServiceDashboardClient>(
static client => client.BaseAddress = new("https+http://_dashboard.basket"));在 AppHost 中声明命名端点:
var basket = builder.AddProject<Projects.BasketService>("basket")
.WithHttpsEndpoint(port: 9999, name: "dashboard");非 .NET 语言如何使用
非 .NET 资源(Node.js、Python、Go 等)同样会在 WithReference 后拿到环境变量。变量名的格式是:
services__{服务名}__{端点名}__{索引}例如:
| 环境变量 | 含义 |
|---|---|
services__basket__https__0 | basket 的 https 端点。 |
services__basket__dashboard__0 | basket 的 dashboard 端点。 |
const basketUrl = process.env.services__basket__https__0
const dashboardUrl = process.env.services__basket__dashboard__0
const response = await fetch(`${basketUrl}/api/items`)import os
import httpx
basket_url = os.getenv("services__basket__https__0")
dashboard_url = os.getenv("services__basket__dashboard__0")
response = httpx.get(f"{basket_url}/api/items")basketURL := os.Getenv("services__basket__https__0")
dashboardURL := os.Getenv("services__basket__dashboard__0")
resp, err := http.Get(basketURL + "/api/items")用配置指定端点
除了环境变量,端点也可以在配置文件中显式指定。Services:{服务名} 节下的键名即端点名,无端点名时使用默认端点:
{
"Services": {
"basket": {
"https": "https://10.2.3.4:8080",
"dashboard": "https://10.2.3.4:9999"
}
}
}解析 https://basket 时会得到 10.2.3.4:8080;解析 https://_dashboard.basket 时会得到 10.2.3.4:9999。
Kubernetes 上的 DNS SRV 解析
部署到 Kubernetes 时,如果服务的 ClusterIP 为 None(Headless Service),可以启用 DNS SRV 解析器,用 SRV 记录发现命名端点:
apiVersion: v1
kind: Service
metadata:
name: basket
spec:
selector:
name: basket-service
clusterIP: None
ports:
- name: default
port: 8080
- name: dashboard
port: 9999在服务端启用解析器:
builder.Services.AddServiceDiscoveryCore();
builder.Services.AddDnsSrvServiceEndpointProvider();约定的端点名 default 对应默认端点(即 https://basket)。
常见问题
报告「无法解析服务地址」
依次确认:
- 消费方确实调用了
WithReference。 - 消费方的
HttpClient配置里启用了服务发现(AddServiceDiscovery(),模板中已由ServiceDefaults处理)。 - 服务名拼写与 AppHost 中声明的一致。
生产环境想用自己的服务发现
Services:* 配置可以被环境变量或配置中心覆盖,也可以注册自定义 IServiceEndpointProvider 接入 Consul、Nacos 等方案。





