GoSuda

Köra en Go-server utökningsbart i dotnet aspire

By snowmerak
views ...

dotnet aspire?

dotnet aspire är ett verktyg som utvecklats för att hjälpa utvecklare med molnbaserad utveckling och konfiguration, i takt med att molnbaserade miljöer blir allt vanligare. Detta verktyg gör det möjligt för .NET-utvecklare att enkelt driftsätta .NET-projekt och olika molnbaserade infrastrukturer, samt tjänster eller containers skrivna i andra språk.

Självklart har Docker till Kubernetes lanserats och drivs, och ett betydande antal sektorer, branscher och utvecklare har flyttat, eller håller på att flytta, från traditionella on-premise-miljöer till molnbaserade miljöer. Detta är nu ett moget område. Därför anser jag att det inte är nödvändigt att förklara de tidigare olägenheterna med värdnamn, portkonfiguration, brandväggar och metrikhantering.

Därför kommer du förmodligen inte att förstå vad dotnet aspire är, även med ovanstående förklaringar. Detta beror på att inte ens Microsoft har gett en exakt definition. Därför kommer jag inte heller att ge någon specifik definition. Däremot kommer jag att använda de grundläggande funktionerna i dotnet aspire som jag har förstått i den här artikeln, så du kan använda detta som referens för att definiera din egen uppfattning.

Projektkonfiguration

Skapa ett dotnet aspire-projekt

Om du inte har dotnet aspire-mallen måste du installera den först. Installera mallen med följande kommando. Om du inte har .NET installerat, vänligen installera det själv.

1dotnet new install Aspire.ProjectTemplates

Skapa sedan en ny lösning i en lämplig mapp.

1dotnet new sln

Därefter, i lösningsmappen, kör du följande kommando för att skapa ett projekt med aspire-apphost-mallen.

1dotnet new aspire-apphost -o AppHost

Då skapas ett aspire-apphost-projekt med endast enkel kod för inställningar.

Lägg till Valkey

Låt oss sedan snabbt lägga till Valkey.

Innan vi lägger till det direkt, erbjuder dotnet aspire olika tredjepartslösningar via community hosting. Självklart kan Valkey också få stöd från denna community hosting, och kan enkelt användas via följande nuget-paket.

1dotnet add package Aspire.Hosting.Valkey

Utöver detta finns det olika integrerade hosting-alternativ, som du kan kontrollera här. Tillbaka till Valkey, öppna filen Program.cs i AppHost-projektet och ändra den enligt följande:

1var builder = DistributedApplication.CreateBuilder(args);
2
3var cache = builder.AddValkey("cache")
4    .WithDataVolume(isReadOnly: false)
5    .WithPersistence(interval: TimeSpan.FromMinutes(5),
6        keysChangedThreshold: 100);
7
8builder.Build().Run();

cache är en implementation av gränssnittet IResourceBuilder som innehåller information för att bygga Valkey-tjänsten.WithDataVolume skapar en volym för att lagra cache-data, och WithPersistence gör det möjligt att lagra cache-data kontinuerligt. Hittills verkar detta ha en liknande roll som volumes i docker-compose. Naturligtvis kan du enkelt skapa detta själv. Men det ligger utanför ramen för denna artikel, så jag kommer inte att diskutera det nu.

Skapa en Go-språk Echo Server

Låt oss sedan lägga till en enkel Go-språkserver. Först, i lösningsmappen, skapar vi en arbetsyta med go work init. För .NET-utvecklare kan en Go-arbetsyta ses som liknande en lösning.

Skapa sedan en mapp som heter EchoServer, flytta in i den och kör go mod init EchoServer. Detta kommando skapar en Go-modul. En modul kan uppfattas som liknande ett projekt för .NET-utvecklare. Skapa sedan filen main.go och skriv följande:

 1package main
 2
 3import (
 4	"log"
 5	"net/http"
 6	"os"
 7)
 8
 9func main() {
10	addr := os.Getenv("PORT")
11	log.Printf("Server started on %s", addr)
12
13	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
14		name := request.URL.Query().Get("name")
15		writer.Write([]byte("Hello, " + name))
16	})
17
18	http.ListenAndServe(":"+addr, nil)
19}

Denna server läser miljövariabeln PORT, som injiceras av Aspire AppHost när den körs, och startar servern på den porten. Det är en enkel server som tar emot en name-fråga och returnerar "Hello, {name}".

Låt oss nu lägga till denna server till dotnet aspire.

Lägga till Echo Server till Aspire

Gå tillbaka till Aspire AppHost-projektet där Valkey lades till, och lägg till community hosting för Go-språket.

1dotnet add package CommunityToolkit.Aspire.Hosting.Golang

Öppna sedan filen Program.cs och lägg till följande kod:

 1var builder = DistributedApplication.CreateBuilder(args);
 2
 3var cache = builder.AddValkey("cache")
 4    .WithDataVolume(isReadOnly: false)
 5    .WithPersistence(interval: TimeSpan.FromMinutes(5),
 6        keysChangedThreshold: 100);
 7
 8var echoServer = builder.AddGolangApp("echo-server", "../EchoServer")
 9    .WithHttpEndpoint(port: 3000, env: "PORT")
10    .WithExternalHttpEndpoints();
11
12builder.Build().Run();

Här är echoServer en implementering av IResourceBuilder-gränssnittet som innehåller information för att bygga Go-språkservern. Den nyligen tillagda metoden AddGolangApp är en utökad metod för den anpassade värden för att lägga till Go-språkservern. Du kan se att den använder port 3000 statiskt och injicerar miljövariabeln PORT. Slutligen gör WithExternalHttpEndpoints den tillgänglig externt.

För att testa, anslut till http://localhost:3000/?name=world och du kommer att se "Hello, world" skrivas ut.

Men för närvarande har dotnet aspire en betydande nackdel för icke-.NET-projekt. Den är följande...

Projektutvidgning

Hur hanterar man då horisontell skalning?

För närvarande erbjuder dotnet aspire alternativet WithReplica endast för byggare av .NET-projekt som läggs till med metoden AddProject. Men för Go-språkhostar eller externa projekt som AddContainer tillhandahålls inte detta alternativ.

Därför måste man implementera det själv med hjälp av en separat lastbalanserare eller reverse proxy. Men om man gör det kan reverse proxyn bli en SPOF (Single Point of Failure), så det är bäst om reverse proxyn erbjuder alternativet WithReplica. Detta innebär att reverse proxyn oundvikligen måste vara ett .NET-projekt.

Hittills har jag använt metoder som nginx, trafik och egen implementation för att lösa detta problem, men med begränsningen att det måste vara ett .NET-projekt hade jag inga omedelbara lösningar till hands. Därför sökte jag efter en reverse proxy implementerad i .NET, och lyckligtvis fanns alternativet YARP. YARP är en reverse proxy implementerad i .NET som också kan fungera som en lastbalanserare och erbjuder en mängd funktioner, vilket gjorde den till ett utmärkt val.

Låt oss nu lägga till YARP.

Konfigurera reverse proxy med YARP

Skapa först ett projekt för att använda YARP.

1dotnet new web -n ReverseProxy

Gå sedan in i projektet och installera YARP.

1dotnet add package Yarp.ReverseProxy --version 2.2.0

När installationen är klar öppnar du filen Program.cs och skriver följande:

 1using Yarp.ReverseProxy.Configuration;
 2
 3var builder = WebApplication.CreateBuilder(args);
 4
 5var routes = new List<RouteConfig>();
 6var clusters = new List<ClusterConfig>();
 7
 8builder.Services.AddReverseProxy()
 9    .LoadFromMemory(routes, clusters);
10
11var app = builder.Build();
12
13app.MapReverseProxy();
14app.Run(url: $"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT") ?? "5000"}");

Denna kod är grundläggande för att använda YARP.routes innehåller information om de rutter som reverse proxyn ska använda, och clusters innehåller information om de kluster som reverse proxyn ska använda. Denna information laddas till reverse proxyn med metoden LoadFromMemory. Slutligen, MapReverseProxy-metoden används för att mappa och köra reverse proxyn.

För faktisk användning, lägg till reverse proxy-projektet som en referens i aspire apphost-projektet, och lägg till och ändra följande kod i filen Program.cs:

1dotnet add reference ../ReverseProxy
1var echoServer = builder.AddGolangApp("echo-server", "../EchoServer")
2    .WithHttpEndpoint(env: "PORT");
3
4var reverseProxy = builder.AddProject<Projects.ReverseProxy>("gateway")
5    .WithReference(echoServer)
6    .WithHttpEndpoint(port: 3000, env: "PORT", isProxied: true)
7    .WithExternalHttpEndpoints();

Nu kan reverse proxyn referera till echo-servern. Strukturen ändras så att inkommande förfrågningar tas emot av reverse proxyn och vidarebefordras till echo-servern.

Ändra Reverse Proxy

Först måste vi ändra lyssningsadressen för projektet som tilldelats reverse proxyn. Ta bort applicationUrl i filen Properties/launchSettings.json. Öppna sedan filen Program.cs och ändra den omfattande enligt följande:

 1using Yarp.ReverseProxy.Configuration;
 2
 3var builder = WebApplication.CreateBuilder(args);
 4
 5var routes = new List<RouteConfig>
 6{
 7    new RouteConfig
 8    {
 9        ClusterId = "cluster-echo",
10        RouteId = "route-echo",
11        Match = new RouteMatch
12        {
13            Path = "/"
14        }
15    }
16};
17
18var echoServerAddr = Environment.GetEnvironmentVariable("services__echo-server__http__0") ?? "http://localhost:8080";
19
20var clusters = new List<ClusterConfig>
21{
22    new ClusterConfig
23    {
24        ClusterId = "cluster-echo",
25        Destinations = new Dictionary<string, DestinationConfig>
26        {
27            { "destination-echo", new DestinationConfig { Address = echoServerAddr } }
28        }
29    }
30};
31
32builder.Services.AddReverseProxy()
33    .LoadFromMemory(routes, clusters);
34
35var app = builder.Build();
36
37app.MapReverseProxy();
38app.Run(url: $"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT") ?? "5000"}");

Först ändrar vi informationen för routes och clusters. Vi lägger till echo-route och echo-cluster för att konfigurera så att förfrågningar skickas till echo-servern. Vi ändrar också så att echo-serverns adress läses från miljövariabler.

Regeln för denna adress är services__{service-name}__http__{index}. För echo-servern är tjänstens namn echo-server, och eftersom det är en enda instans används 0 som index. Om du skulle lägga till en ASP.NET Core-server, kan flera instanser skapas via WithReplica, så indexet skulle ökas.http://localhost:8080, som hanteras som ett undantag, är ett meningslöst skräpvärde.

Kör nu projektet och anslut till http://localhost:3000/?name=world, så kommer du att se "Hello, world" skrivas ut igen.

Utökningsidéer

Nu har vi lagt till en Go-server till dotnet aspire och bekräftat att förfrågningar skickas via en reverse proxy. Då kan vi utöka denna process för att implementera den programmatiskt. Till exempel kan vi generera flera instanser av echo-servern genom att lägga till numrering efter tjänstens namn och automatiskt lägga till konfiguration för reverse proxyn.

Ändra koden som använder reverse proxyn och echo-servern i filen Program.cs i aspire apphost-projektet enligt följande:

 1var reverseProxy = builder.AddProject<Projects.ReverseProxy>("gateway")
 2    .WithHttpEndpoint(port: 3000, env: "PORT", isProxied: true)
 3    .WithExternalHttpEndpoints();
 4
 5for (var i = 0; i < 8; i++)
 6{
 7    var echoServer = builder.AddGolangApp($"echo-server-{i}", "../EchoServer")
 8        .WithHttpEndpoint(env: "PORT");
 9    reverseProxy.WithReference(echoServer);
10}

Och ändra filen Program.cs i reverse proxy-projektet enligt följande:

 1var echoServerDestinations = new Dictionary<string, DestinationConfig>();
 2for (var i = 0; i < 8; i++)
 3{
 4    echoServerDestinations[$"destination-{i}"] = new DestinationConfig
 5    {
 6        Address = Environment.GetEnvironmentVariable($"services__echo-server-{i}__http__0") ?? "http://localhost:8080"
 7    };
 8}
 9
10var clusters = new List<ClusterConfig>
11{
12    new ClusterConfig
13    {
14        ClusterId = "cluster-echo",
15        Destinations = echoServerDestinations
16    }
17};

Vi lägger till destinationsinställningar för de 8 utökade echo-serverinstanserna. Nu har reverse proxyn destinationsinformation för de utökade echo-servrarna och kan vidarebefordra förfrågningar. Om du ansluter till den befintliga http://localhost:3000/?name=world kommer du fortfarande att se "Hello, world" skrivas ut.

Avslutning

I den här artikeln har jag förklarat processen för att lägga till en Go-server till dotnet aspire och vidarebefordra förfrågningar via en reverse proxy. Jag har dock inte skrivit allt om utökningar, och jag har skapat ett separat arkiv med ett exempel som kan implementeras mer programmatiskt via miljövariabler. För detaljerad projektkonfiguration och kod, se snowmerak/AspireStartPack.

Personligen förväntar jag mig att dotnet aspire kan spela sin egen roll som ett alternativ till Docker Compose och som ett verktyg för molndistribution. Eftersom det redan finns generatorer som skapar Docker Compose- eller Kubernetes-manifest, tror jag att tillgängligheten till infrastrukturverktyg har förbättrats för den genomsnittlige utvecklaren.