Engineering

Dotnet Aspire - Service Discovery

Heute vereinfachen wir die Konfiguration von Abhängigkeiten in unserem bestehenden Dotnet Projekt. Das bringt uns nicht nur unserem Ziel näher in 60 Sekunden vom Checkout zur lauffähigen Applikation auf einem Developer Rechner zu kommen, es vereinfacht auch die Arbeit aller Entwickler im Projekt.

Informationen in der Aspire Konfiguration

Im letzten Artikel zur Basiskonfiguration haben wir uns darum gekümmert unser Projekt und Abhängigkeiten zu konfigurieren, sodass diese bei der Ausführung gemeinsam gestartet werden.

Das war die Konfiguration, die wir dazu verwendet hatten:

using Microsoft.Extensions.Configuration;
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var smtp = builder.AddContainer("smtp", "mailhog/mailhog", "latest")
        .WithEndpoint(name: "smtp-endpoint", port: 1025, targetPort: 1025) 
        .WithHttpEndpoint(8025, 8025);                                     

var databaseInstance = builder.AddPostgres("database")
        .WithOtlpExporter()
        .WithEnvironment("POSTGRES_DB", "Web");                            
var database = databaseInstance.AddDatabase("DB", "Web");                   

builder.AddProject<Web>("Web")
        .WithReference(database)
        .WithReference(smtp.GetEndpoint("smtp-endpoint"));

builder.Build().Run();

Aspire Distributed Applications wissen viel über die Services, die sie ausführen:

  • welche Art von Applikationen laufen,
  • welche Ports verwenden sie
  • und welche Benutzerdaten benötigt werden, um sich anzumelden.

Konfiguration eines Webprojektes

Durch WithReference geben wir an, dass diese Ressourcen direkte Abhängigkeiten sind und von unserem Projekt verwendet werden.

Das bedeutet, dass es in unserem Projekt Code gibt, der diese Konfigurationsdaten braucht um z. B. auf die Datenbank zugreifen zu können.

Für den Zugriff auf die Datenbank braucht die Dotnet-Applikation einen ConnectionString. Aspire stellt diesen unter dem Namen der Ressource zur Verfügung.

Damit kann die Applikation den ConnectionString mit dem Namen der Ressource abrufen.

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetRequiredConnectionString("DB");

Microsoft stellt aber auch zusätzliche Packages bereit, die die Konfiguration von z. B. einer NpsqlDataSource, das funktioniert mit Aspire.Npsql oder Aspire.Npgsql.EntityFrameworkCore.PostgreSQL:

var builder = WebApplication.CreateBuilder(args);

builder.AddNpgsqlDataSource("Web");
builder.AddNpgsqlDbContext<MyContext>("Web");

So wird einfach eine NpsqlDataSource und ein Efcore-DbContext registriert, welcher für die Aspire Postgres-Datenbank konfiguriert ist.

Die Aspire-Konfigurationspackages sind darauf ausgelegt, mit den seit .NET 6.0 existierenden WebApplicationBuilder zu arbeiten.

Arbeitet ihr noch mit Startup-Klassen müsst ihr der bestehende Konfigurationscode auf das neue System umstellen, um die Aspire-Konfiguration verwenden zu können.

Im Vergleich zu Docker Compose oder anderen Systemen, erspart ihr euch, den ConnectionString in der Konfiguration manuell aufzubauen.

Service Discovery per Uri Scheme

Der andere Weg ist Service Discovery, die Uri Addressable sind, ist per Http Forwarding. Aspire erstellt für jeden Service einen Proxy, der die Network Requests übernimmt und an die Services weiterleitet.

Es registriert auch einen HttpClientHandler, der speziell formatierte Uri anhand vom Scheme erkennt, und über den Proxy an den richtigen Hostport weiterleitet.

Das Schema dazu ist scheme+http://servicename oder scheme+http://_endpointname.servicename.

Diese Form der Servicediscovery scheint vom Konzept noch nicht ausgereift zu sein.

Sie braucht die Unterstützung des HttpClients und muss entsprechend konfiguriert sein. Damit bietet sie auch keinen Mehrwert für Services die nicht Aspire-Aware sind Für Services die bereits Aspire-Aware sind, würde ich derzeit die erste Variante verwenden.