Skip to content

服务发现 ​

更新: 2026-10-16 15:20:09   字数: 0 字   时长: 0 分钟

服务发现解决的是「服务之间怎么找到对方」。有了它,前端不需要硬编码 http://localhost:5001,只要引用服务名 catalog,Aspire 会在运行时解析出真实地址。

工作原理 ​

当您用 WithReference 连接两个服务时,Aspire 自动完成四件事:

  1. AppHost 声明资源并分配端点。
  2. 通过环境变量把配置注入到每个服务中。
  3. 您的代码使用逻辑名称,例如 https+http://catalog。
  4. Aspire 的解析器在运行时把名称翻译成真实地址。

关键点:代码里始终使用逻辑服务名,由配置驱动的端点解析器在运行时完成翻译。

csharp
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 启用服务发现与弹性策略。声明一个强类型客户端即可:

csharp
builder.Services.AddHttpClient<CatalogServiceClient>(
    static client => client.BaseAddress = new("https+http://catalog"));

https+http:// 的含义是:优先使用 HTTPS,如果对方只提供 HTTP 则自动回退。这是本地开发中最常用的写法。

命名端点 ​

有些服务会暴露多个端点(例如一个业务端点加一个管理端点)。此时在 URI 的 host 部分写端点名即可,格式为 scheme://_endpointName.serviceName:

csharp
// 默认端点
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 中声明命名端点:

csharp
var basket = builder.AddProject<Projects.BasketService>("basket")
    .WithHttpsEndpoint(port: 9999, name: "dashboard");

非 .NET 语言如何使用 ​

非 .NET 资源(Node.js、Python、Go 等)同样会在 WithReference 后拿到环境变量。变量名的格式是:

text
services__{服务名}__{端点名}__{索引}

例如:

环境变量含义
services__basket__https__0basket 的 https 端点。
services__basket__dashboard__0basket 的 dashboard 端点。
typescript
const basketUrl = process.env.services__basket__https__0
const dashboardUrl = process.env.services__basket__dashboard__0

const response = await fetch(`${basketUrl}/api/items`)
python
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")
go
basketURL := os.Getenv("services__basket__https__0")
dashboardURL := os.Getenv("services__basket__dashboard__0")

resp, err := http.Get(basketURL + "/api/items")

用配置指定端点 ​

除了环境变量,端点也可以在配置文件中显式指定。Services:{服务名} 节下的键名即端点名,无端点名时使用默认端点:

json
{
  "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 记录发现命名端点:

yaml
apiVersion: v1
kind: Service
metadata:
  name: basket
spec:
  selector:
    name: basket-service
  clusterIP: None
  ports:
    - name: default
      port: 8080
    - name: dashboard
      port: 9999

在服务端启用解析器:

csharp
builder.Services.AddServiceDiscoveryCore();
builder.Services.AddDnsSrvServiceEndpointProvider();

约定的端点名 default 对应默认端点(即 https://basket)。

常见问题 ​

报告「无法解析服务地址」

依次确认:

  1. 消费方确实调用了 WithReference。
  2. 消费方的 HttpClient 配置里启用了服务发现(AddServiceDiscovery(),模板中已由 ServiceDefaults 处理)。
  3. 服务名拼写与 AppHost 中声明的一致。
生产环境想用自己的服务发现

Services:* 配置可以被环境变量或配置中心覆盖,也可以注册自定义 IServiceEndpointProvider 接入 Consul、Nacos 等方案。

下一步 ​

你觉得这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度