Microsoft.Extensions.ServiceDiscovery 8.0.0-preview.7.24251.11

The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org. Prefix Reserved
This is a prerelease version of Microsoft.Extensions.ServiceDiscovery.
dotnet add package Microsoft.Extensions.ServiceDiscovery --version 8.0.0-preview.7.24251.11
NuGet\Install-Package Microsoft.Extensions.ServiceDiscovery -Version 8.0.0-preview.7.24251.11
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.7.24251.11" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Microsoft.Extensions.ServiceDiscovery --version 8.0.0-preview.7.24251.11
#r "nuget: Microsoft.Extensions.ServiceDiscovery, 8.0.0-preview.7.24251.11"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Microsoft.Extensions.ServiceDiscovery as a Cake Addin
#addin nuget:?package=Microsoft.Extensions.ServiceDiscovery&version=8.0.0-preview.7.24251.11&prerelease

// Install Microsoft.Extensions.ServiceDiscovery as a Cake Tool
#tool nuget:?package=Microsoft.Extensions.ServiceDiscovery&version=8.0.0-preview.7.24251.11&prerelease

Microsoft.Extensions.ServiceDiscovery

The Microsoft.Extensions.ServiceDiscovery library is designed to simplify the integration of service discovery patterns in .NET applications. Service discovery is a key component of most distributed systems and microservices architectures. This library provides a straightforward way to resolve service names to endpoint addresses.

In typical systems, service configuration changes over time. Service discovery accounts for by monitoring endpoint configuration using push-based notifications where supported, falling back to polling in other cases. When endpoints are refreshed, callers are notified so that they can observe the refreshed results.

How it works

Service discovery uses configured providers to resolve service endpoints. When service endpoints are resolved, each registered provider is called in the order of registration to contribute to a collection of service endpoints (an instance of ServiceEndpointSource).

Providers implement the IServiceEndpointProvider interface. They are created by an instance of IServiceEndpointProviderProvider, which are registered with the .NET dependency injection system.

Developers typically add service discovery to their HttpClient using the IHttpClientFactory with the AddServiceDiscovery extension method.

Services can be resolved directly by calling ServiceEndpointResolver's GetEndpointsAsync method, which returns a collection of resolved endpoints.

Change notifications

Service configuration can change over time. Service discovery accounts for by monitoring endpoint configuration using push-based notifications where supported, falling back to polling in other cases. When endpoints are refreshed, callers are notified so that they can observe the refreshed results. To subscribe to notifications, callers use the ChangeToken property of ServiceEndpointCollection. For more information on change tokens, see Detect changes with change tokens in ASP.NET Core.

Extensibility using features

Service endpoints (ServiceEndpoint instances) and collections of service endpoints (ServiceEndpointCollection instances) expose an extensible IFeatureCollection via their Features property. Features are exposed as interfaces accessible on the feature collection. These interfaces can be added, modified, wrapped, replaced or even removed at resolution time by providers. Features which may be available on a ServiceEndpoint include:

Resolution order

The providers included in the Microsoft.Extensions.ServiceDiscovery series of packages skip resolution if there are existing endpoints in the collection when they are called. For example, consider a case where the following providers are registered: Configuration, DNS SRV, Pass-through. When resolution occurs, the providers will be called in-order. If the Configuration providers discovers no endpoints, the DNS SRV provider will perform resolution and may add one or more endpoints. If the DNS SRV provider adds an endpoint to the collection, the Pass-through provider will skip its resolution and will return immediately instead.

Getting Started

Installation

To install the library, use the following NuGet command:

dotnet add package Microsoft.Extensions.ServiceDiscovery

Usage example

In the Program.cs file of your project, call the AddServiceDiscovery extension method to add service discovery to the host, configuring default service endpoint providers.

builder.Services.AddServiceDiscovery();

Add service discovery to an individual IHttpClientBuilder by calling the AddServiceDiscovery extension method:

builder.Services.AddHttpClient<CatalogServiceClient>(c =>
{
  c.BaseAddress = new("http://catalog"));
}).AddServiceDiscovery();

Alternatively, you can add service discovery to all HttpClient instances by default:

builder.Services.ConfigureHttpClientDefaults(http =>
{
    // Turn on service discovery by default
    http.AddServiceDiscovery();
});

Resolving service endpoints from configuration

The AddServiceDiscovery extension method adds a configuration-based endpoint provider by default. This provider reads endpoints from the .NET Configuration system. The library supports configuration through appsettings.json, environment variables, or any other IConfiguration source.

Here is an example demonstrating how to configure a endpoints for the service named catalog via appsettings.json:

{
  "Services": {
      "catalog": [
        "localhost:8080",
        "10.46.24.90:80",
      ]
    }
}

The above example adds two endpoints for the service named catalog: localhost:8080, and "10.46.24.90:80". Each time the catalog is resolved, one of these endpoints will be selected.

If service discovery was added to the host using the AddServiceDiscoveryCore extension method on IServiceCollection, the configuration-based endpoint provider can be added by calling the AddConfigurationServiceEndpointProvider extension method on IServiceCollection.

Configuration

The configuration provider is configured using the ConfigurationServiceEndpointProviderOptions class, which offers these configuration options:

  • SectionName: The name of the configuration section that contains service endpoints. It defaults to "Services".

  • ShouldApplyHostNameMetadata: A delegate used to determine if host name metadata should be applied to resolved endpoints. It defaults to a function that returns false.

To configure these options, you can use the Configure extension method on the IServiceCollection within your application's startup class or main program file:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ConfigurationServiceEndpointProviderOptions>(options =>
{
    options.SectionName = "MyServiceEndpoints";

    // Configure the logic for applying host name metadata
    options.ShouldApplyHostNameMetadata = endpoint =>
    {
        // Your custom logic here. For example:
        return endpoint.Endpoint is DnsEndPoint dnsEp && dnsEp.Host.StartsWith("internal");
    };
});

This example demonstrates setting a custom section name for your service endpoints and providing a custom logic for applying host name metadata based on a condition.

Resolving service endpoints using platform-provided service discovery

Some platforms, such as Azure Container Apps and Kubernetes (if configured), provide functionality for service discovery without the need for a service discovery client library. When an application is deployed to one of these environments, it may be preferable to use the platform's existing functionality instead. The pass-through provider exists to support this scenario while still allowing other provider (such as configuration) to be used in other environments, such as on the developer's machine, without requiring a code change or conditional guards.

The pass-through provider performs no external resolution and instead resolves endpoints by returning the input service name represented as a DnsEndPoint.

The pass-through provider is configured by-default when adding service discovery via the AddServiceDiscovery extension method.

If service discovery was added to the host using the AddServiceDiscoveryCore extension method on IServiceCollection, the pass-through provider can be added by calling the AddPassThroughServiceEndpointProvider extension method on IServiceCollection.

In the case of Azure Container Apps, the service name should match the app name. For example, if you have a service named "basket", then you should have a corresponding Azure Container App named "basket".

Load-balancing with endpoint selectors

Each time an endpoint is resolved by the HttpClient pipeline, a single endpoint will be selected from the set of all known endpoints for the requested service. If multiple endpoints are available, it may be desirable to balance traffic across all such endpoints. To accomplish this, a customizable endpoint selector can be used. By default, endpoints are selected in round-robin order. To use a different endpoint selector, provide an IServiceEndpointSelector instance to the AddServiceDiscovery method call. For example, to select a random endpoint from the set of resolved endpoints, specify RandomServiceEndpointSelector.Instance as the endpoint selector:

builder.Services.AddHttpClient<CatalogServiceClient>(
    static client => client.BaseAddress = new("http://catalog"));
  .AddServiceDiscovery(RandomServiceEndpointSelector.Instance);

The Microsoft.Extensions.ServiceDiscovery package includes the following endpoint selector providers:

  • Pick-first, which always selects the first endpoint: PickFirstServiceEndpointSelectorProvider.Instance
  • Round-robin, which cycles through endpoints: RoundRobinServiceEndpointSelectorProvider.Instance
  • Random, which selects endpoints randomly: RandomServiceEndpointSelectorProvider.Instance
  • Power-of-two-choices, which attempts to pick the least heavily loaded endpoint based on the Power of Two Choices algorithm for distributed load balancing, degrading to randomly selecting an endpoint when either of the provided endpoints do not have the IEndpointLoadFeature feature: PowerOfTwoChoicesServiceEndpointSelectorProvider.Instance

Endpoint selectors are created via an IServiceEndpointSelectorProvider instance, such as those listed above. The provider's CreateSelector() method is called to create a selector, which is an instance of IServiceEndpointSelector. The IServiceEndpointSelector instance is given the set of known endpoints when they are resolved, using the SetEndpoints(ServiceEndpointCollection collection) method. To choose an endpoint from the collection, the GetEndpoint(object? context) method is called, returning a single ServiceEndpoint. The context value passed to GetEndpoint is used to provide extra context which may be useful to the selector. For example, in the HttpClient case, the HttpRequestMessage is passed. None of the provided implementations of IServiceEndpointSelector inspect the context, and it can be ignored unless you are using a selector which does make use of it.

Service discovery in .NET Aspire

.NET Aspire includes functionality for configuring the service discovery at development and testing time. This functionality works by providing configuration in the format expected by the configuration-based endpoint provider described above from the .NET Aspire AppHost project to the individual service projects added to the application model.

Configuration for service discovery is only added for services which are referenced by a given project. For example, consider the following AppHost program:

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);

In the above example, the frontend project references the catalog project and the basket project. The two WithReference calls instruct the .NET Aspire application to pass service discovery information for the referenced projects (catalog, and basket) into the frontend project.

Named endpoints

Some services expose multiple, named endpoints. Named endpoints can be resolved by specifying the endpoint name in the host portion of the HTTP request URI, following the format http://_endpointName.serviceName. For example, if a service named "basket" exposes an endpoint named "dashboard", then the URI http://_dashboard.basket can be used to specify this endpoint, for example:

builder.Services.AddHttpClient<BasketServiceClient>(
    static client => client.BaseAddress = new("http://basket"));
builder.Services.AddHttpClient<BasketServiceDashboardClient>(
    static client => client.BaseAddress = new("http://_dashboard.basket"));

In the above example, two HttpClients are added: one for the core basket service and one for the basket service's dashboard.

Named endpoints using configuration

With the configuration-based endpoint provider, named endpoints can be specified in configuration by prefixing the endpoint value with _endpointName., where endpointName is the endpoint name. For example, consider this appsettings.json configuration which defined a default endpoint (with no name) and an endpoint named "dashboard":

{
  "Services": {
    "basket": [
      "10.2.3.4:8080", /* the default endpoint, when resolving http://basket */
      "_dashboard.10.2.3.4:9999" /* the "dashboard" endpoint, resolved via http://_dashboard.basket */
    ]
  }
}

Named endpoints in .NET Aspire

.NET Aspire uses the configuration-based provider at development and testing time, providing convenient APIs for configuring named endpoints which are then translated into configuration for the target services. For example:

var builder = DistributedApplication.CreateBuilder(args);

var basket = builder.AddProject<Projects.BasketService>("basket")
    .WithEndpoint(hostPort: 9999, scheme: "http", name: "admin");

var adminDashboard = builder.AddProject<Projects.MyDashboardAggregator>("admin-dashboard")
       .WithReference(basket.GetEndpoint("admin"));

var frontend = builder.AddProject<Projects.Frontend>("frontend")
       .WithReference(basket);

In the above example, the "basket" service exposes an "admin" endpoint in addition to the default "http" endpoint which it exposes. This endpoint is consumed by the "admin-dashboard" project, while the "frontend" project consumes all endpoints from "basket". Alternatively, the "frontend" project could be made to consume only the default "http" endpoint from "basket" by using the GetEndpoint(string name) method, as in the following example:


// The preceding code is the same as in the above sample

var frontend = builder.AddProject<Projects.Frontend>("frontend")
       .WithReference(basket.GetEndpoint("http"));

Named endpoints in Kubernetes using DNS SRV

When deploying to Kubernetes, the DNS SRV service endpoint provider can be used to resolve named endpoints. For example, the following resource definition will result in a DNS SRV record being created for an endpoint named "default" and an endpoint named "dashboard", both on the service named "basket".

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

To configure a service to resolve the "dashboard" endpoint on the "basket" service, add the DNS SRV service endpoint provider to the host builder as follows:

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

The special port name "default" is used to specify the default endpoint, resolved using the URI http://basket.

As in the previous example, add service discovery to an HttpClient for the basket service:

builder.Services.AddHttpClient<BasketServiceClient>(
    static client => client.BaseAddress = new("http://basket"));

Similarly, the "dashboard" endpoint can be targeted as follows:

builder.Services.AddHttpClient<BasketServiceDashboardClient>(
    static client => client.BaseAddress = new("http://_dashboard.basket"));

Named endpoints in Azure Container Apps

Named endpoints are not currently supported for services deployed to Azure Container Apps.

Feedback & contributing

https://github.com/dotnet/aspire

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on Microsoft.Extensions.ServiceDiscovery:

Package Downloads
Microsoft.Extensions.ServiceDiscovery.Yarp The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

Provides extensions for service discovery for the YARP reverse proxy.

Rocket.Surgery.LaunchPad.Foundation

Package Description

Microsoft.Extensions.ServiceDiscovery.Dns The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

Provides extensions to HttpClient to resolve well-known hostnames to concrete endpoints based on DNS records. Useful for service resolution in orchestrators such as Kubernetes.

Hexalith.Infrastructure.AspireService.Defaults

Hexalith is a set of libraries to build a micro-service architecture.

HolisticWare.Tools.Clients.Maui.Aspire.Integration The ID prefix of this package has been reserved for one of the owners of this package by NuGet.org.

C# (managed) API for MAUI and Aspire integration

GitHub repositories (7)

Showing the top 5 popular GitHub repositories that depend on Microsoft.Extensions.ServiceDiscovery:

Repository Stars
dotnet/eShop
A reference .NET application implementing an eCommerce site
dotnet/aspire
An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
CodeMazeBlog/CodeMazeGuides
The main repository for all the Code Maze guides
dotnet/aspire-samples
JasonBock/Rocks
A mocking library based on the Compiler APIs (Roslyn + Mocks)
Version Downloads Last updated
8.0.0-preview.7.24251.11 9,580 5/7/2024
8.0.0-preview.6.24214.1 17,679 4/23/2024
8.0.0-preview.5.24201.12 28,315 4/9/2024
8.0.0-preview.4.24156.9 37,896 3/12/2024
8.0.0-preview.3.24105.21 52,042 2/13/2024
8.0.0-preview.2.23619.3 65,593 12/20/2023
8.0.0-preview.1.23557.2 57,185 11/14/2023