Utfør en Go-server i .NET Aspire på en utvidbar måte
dotnet aspire?
dotnet aspire er et verktøy som er laget for å hjelpe utviklere med utvikling og konfigurasjon av skybaserte applikasjoner, ettersom skybaserte miljøer stadig øker. Dette verktøyet gjør det mulig for .NET-utviklere å enkelt distribuere .NET-prosjekter og diverse skybasert infrastruktur, samt tjenester eller containere skrevet i andre språk.
Naturligvis blir det lansert og operert fra Docker til k8s, og mange felt, industrier og utviklere i eksisterende on-premise miljøer går over til, og er i ferd med å gå over til, skybaserte miljøer. Det er nå et modent felt. Derfor tror jeg det ikke er nødvendig å forklare de eksisterende ulempene med hensyn til hostnavn, portkonfigurasjon, brannmurer, metrikkadministrasjon osv.
Så, ut fra de ovennevnte forklaringene, vil du neppe få en følelse av hva dotnet aspire egentlig er. Det er fordi Microsoft ikke gir en nøyaktig definisjon selv. Derfor vil jeg heller ikke gi noen spesiell definisjon. Jeg vil imidlertid bruke de grunnleggende funksjonene til dotnet aspire slik jeg har forstått det i denne artikkelen, så vennligst bruk det som referanse for å finne din egen posisjon.
Prosjektoppsett
Opprett dotnet aspire-prosjekt
Hvis du ikke har dotnet aspire-malen, må du først installere malen. Installer malen med følgende kommando. Hvis du ikke har .net, må du installere det selv.
1dotnet new install Aspire.ProjectTemplates
Og opprett en ny løsning i en passende mappe.
1dotnet new sln
Etter det, kjør følgende kommando i løsningsmappen for å opprette et prosjekt med aspire-apphost-malen.
1dotnet new aspire-apphost -o AppHost
Da opprettes et aspire-apphost-prosjekt som bare inneholder enkel kode for oppsett.
Legg til Valkey
La oss enkelt legge til Valkey.
Før du legger det til, tilbyr dotnet aspire ulike tredjepartsløsninger gjennom community hosting. Naturligvis kan Valkey også motta støtte fra denne community hostingen, og kan enkelt brukes gjennom følgende nuget-pakke.
1dotnet add package Aspire.Hosting.Valkey
Den tilbyr også ulike integrerte hostinger, som du kan sjekke her. Gå tilbake til Valkey og åpne Program.cs-filen i AppHost-prosjektet og endre den som følger.
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
er en implementering av IResourceBuilder
-grensesnittet som inneholder informasjon for å bygge valkey-tjenesten.WithDataVolume
oppretter et volum for å lagre cachedata, og WithPersistence
lar deg lagre cachedata kontinuerlig.
Så langt ser det ut til å ha en lignende rolle som volumes
i docker-compose
.
Naturligvis kan du enkelt lage dette selv.
Men det går utover omfanget av denne artikkelen, så jeg vil ikke snakke om det nå.
Opprett Go-språk ekkoserver
La oss legge til en enkel Go-språkserver.
Først oppretter du et arbeidsområde gjennom go work init
i løsningsmappen.
For .NET-utviklere kan du tenke på et Go-arbeidsområde som noe som ligner på en løsning.
Og lag en mappe som heter EchoServer, gå inn i den og kjør go mod init EchoServer
.
Denne kommandoen oppretter en Go-modul. Du kan se på moduler som noe som ligner på et prosjekt for .NET-utviklere.
Og lag en main.go
-fil og skriv den som følger.
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}
Denne serveren kjører serveren ved å lese PORT
-miljøvariabelen, som Aspire AppHost vil injisere når den kjøres, og lytter til den porten.
Det er en server som enkelt mottar en name
-spørring og returnerer Hello, {name}
.
La oss nå legge til denne serveren i dotnet aspire.
Legg til ekkoserveren i aspire
Gå tilbake til Aspire AppHost-prosjektet der du la til Valkey og legg til community hosting for Go-språket.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
Og åpne Program.cs-filen og legg til følgende kode.
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();
Her er echoServer
en implementering av IResourceBuilder
-grensesnittet som inneholder informasjon for å bygge Go-språkserveren.
Metoden AddGolangApp
du nettopp la til, er en utvidelsesmetode for en tilpasset host for å legge til Go-språkserveren.
Den bruker fast 3000-port og du kan se at den injiserer PORT
-miljøvariabelen.
Til slutt er WithExternalHttpEndpoints
for å tillate ekstern tilgang.
For å teste, hvis du kobler til http://localhost:3000/?name=world
, vil du kunne se at Hello, world
skrives ut.
Men for øyeblikket har dotnet aspire en tung straff som gis til non-dotnet-prosjekter. Det er...
Prosjektutvidelse
Hvordan skalerer du horisontalt?
For øyeblikket tilbyr dotnet aspire bare WithReplica
-alternativet for byggere for .NET-prosjekter som er lagt til med AddProject
-metoden.
Imidlertid tilbyr den ikke dette alternativet for Go-språkhoster eller eksterne prosjekter som AddContainer
.
Derfor må du implementere det direkte ved hjelp av en separat load balancer eller reverse proxy.
Men hvis dette er tilfelle, kan den reverse proxyen bli en SPOF, så det er lurt at reverse proxyen tilbyr WithReplica
-alternativet.
Da må reverse proxyen nødvendigvis være et .NET-prosjekt.
Så langt har vi brukt metoder som nginx, trafik eller direkte implementering for dette problemet, men når begrensningen av et .NET-prosjekt er på plass, hadde jeg ingen umiddelbare løsninger. Så jeg lette etter en reverse proxy implementert i .NET, og heldigvis var det et alternativ som heter YARP. YARP er en reverse proxy implementert i .NET, kan også fungere som en load balancer og tilbyr ulike funksjoner, så jeg mente det var et godt valg.
La oss nå legge til YARP.
Konfigurer reverse proxy med YARP
Først oppretter du et prosjekt for å bruke YARP.
1dotnet new web -n ReverseProxy
Og flytt til prosjektet og installer YARP.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
Når installasjonen er fullført, åpner du Program.cs-filen og skriver den som følger.
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"}");
Denne koden er grunnleggende kode for å bruke YARP.routes
inneholder ruteinformasjonen som reverse proxyen vil bruke, og clusters
inneholder klyngeinformasjonen som reverse proxyen vil bruke.
Denne informasjonen lastes inn i reverse proxyen med LoadFromMemory
-metoden.
Til slutt kartlegges og kjøres reverse proxyen ved hjelp av MapReverseProxy
-metoden.
Og for praktisk bruk legger du til reverse proxy-prosjektet som en referanse i aspire apphost-prosjektet, og legger til og endrer følgende kode i Program.cs-filen.
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();
Nå kan reverse proxyen referere til ekkoserveren. Strukturen endres slik at forespørsler som kommer fra utsiden mottas av reverse proxyen og sendes videre til ekkoserveren.
Endre reverse proxy
Først må du endre lytteadressen til prosjektet som er tilordnet reverse proxyen.
Fjern applicationUrl
i Properties/launchSettings.json
-filen.
Og åpne Program.cs-filen og endre den drastisk som følger.
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 endrer du informasjonen for routes
og clusters
.
Legg til henholdsvis echo-route
og echo-cluster
for å sende forespørsler til ekkoserveren.
Og endre den for å lese og bruke adressen til ekkoserveren fra miljøvariabelen.
Regelen for denne adressen er services__{service-name}__http__{index}
.
Når det gjelder ekkoserveren, er tjenestenavnet echo-server
, og siden det er en enkelt instans, brukes 0
som indeks.
Hvis du legger til en asp .net core-server, kan flere instanser opprettes gjennom WithReplica
, så du kan bruke den ved å øke indeksen.
Unntaksbehandlingen http://localhost:8080
er en verdiløs søppelverdi.
Nå, hvis du kjører prosjektet og kobler til http://localhost:3000/?name=world
, vil du fortsatt kunne se at Hello, world
skrives ut.
Utvidelsesideer
Nå har du bekreftet at du kan legge til en Go-server i dotnet aspire og videresende forespørsler gjennom en reverse proxy. Da vil du nå kunne utvide dette for å implementere det programmatisk. Du kan for eksempel opprette flere instanser ved å legge til et nummer bak tjenestenavnet for ekkoserveren, og automatisk legge til konfigurasjonen for reverse proxyen.
Endre koden som bruker reverse proxyen og ekkoserveren i Program.cs-filen i aspire apphost-prosjektet som følger.
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}
Og endre Program.cs-filen i reverse proxy-prosjektet som følger.
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};
Legg til destinasjonsinnstillinger for de 8 økte ekkoserverinstansene.
Nå har reverse proxyen destinasjonsinformasjon for de økte ekkoserverne og kan videresende forespørsler.
Hvis du kobler til den eksisterende http://localhost:3000/?name=world
, vil du fortsatt kunne se at Hello, world
skrives ut.
Avslutter
I denne artikkelen har jeg forklart prosessen med å legge til en Go-server i dotnet aspire og videresende forespørsler gjennom en reverse proxy. Utvidelsen er imidlertid ikke fullført ennå, og jeg har skrevet et eksempel som kan implementeres mer programmatisk gjennom miljøvariabler i et eget depot. Se snowmerak/AspireStartPack for detaljert prosjektoppsett og kode.
Personlig forventer jeg at dotnet aspire kan spille sin egen rolle som et alternativ til docker compose og som et skybasert distribusjonsverktøy. Det finnes allerede en generator som genererer docker compose eller k8s manifest, så jeg tror at tilgjengeligheten til infrastrukturverktøy har blitt bedre for vanlige utviklere.