Een schaalbare Go-server uitvoeren in .NET Aspire
dotnet aspire?
dotnet aspire is een hulpmiddel dat is ontwikkeld om ontwikkelaars te helpen met de ontwikkeling en configuratie van cloud-native applicaties, in een omgeving waar cloud-native omgevingen steeds meer voorkomen. Dit hulpmiddel stelt .NET-ontwikkelaars in staat om eenvoudig .NET-projecten en diverse cloud-native infrastructuren, evenals diensten of containers in andere talen, te implementeren.
Uiteraard wordt het uitgebracht en beheerd van docker tot k8s, en een aanzienlijk aantal sectoren, industrieën en ontwikkelaars zijn van de traditionele on-premise omgeving overgestapt naar cloud-native omgevingen, of zijn al overgestapt. Het is nu een volwassen gebied. Daarom denk ik dat het niet nodig is om de bestaande ongemakken te beschrijven met betrekking tot hostnamen, poortconfiguraties, firewalls en metrische gegevensbeheer.
Dus, gezien de bovenstaande uitleg, zult u waarschijnlijk geen idee hebben wat dotnet aspire is. Dit komt omdat Microsoft zelf geen exacte definitie heeft gegeven. Daarom zal ik ook geen specifieke definitie geven. Ik zal echter de basisfunctionaliteiten van dotnet aspire gebruiken zoals ik ze begrijp in dit artikel, dus ik denk dat het nuttig zal zijn als u dit gebruikt om uw eigen positie te bepalen.
Projectconfiguratie
dotnet aspire project aanmaken
Als je geen dotnet aspire template hebt, moet je eerst de template installeren. Installeer de template met de volgende opdracht. Als .net niet is geïnstalleerd, installeer het dan zelf.
1dotnet new install Aspire.ProjectTemplates
Maak vervolgens een nieuwe oplossing in een geschikte map.
1dotnet new sln
Voer daarna de volgende opdracht uit in de oplossingsmap om een project met de aspire-apphost template aan te maken.
1dotnet new aspire-apphost -o AppHost
Er wordt een aspire-apphost project aangemaakt dat alleen eenvoudige code voor de instellingen bevat.
Valkey toevoegen
Laten we nu eenvoudig Valkey toevoegen.
Voordat we dit toevoegen, biedt dotnet aspire verschillende oplossingen van derden via community hosting. Uiteraard kan valkey ook ondersteuning van deze community hosting ontvangen, en het kan gemakkelijk worden gebruikt via het volgende nuget-pakket.
1dotnet add package Aspire.Hosting.Valkey
Er zijn ook diverse andere geïntegreerde hostingopties beschikbaar, die je hier kunt vinden. Laten we terugkeren naar valkey en het Program.cs bestand in het AppHost project openen en als volgt aanpassen:
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
is een implementatie van de IResourceBuilder
interface met informatie over het bouwen van de valkey-service.WithDataVolume
creëert een volume voor het opslaan van cachegegevens, en WithPersistence
zorgt ervoor dat cachegegevens continu kunnen worden opgeslagen.
Tot nu toe lijkt dit op de volumes
in docker-compose
.
Uiteraard kunt u dit ook gemakkelijk zelf maken.
Dit valt echter buiten het bestek van dit artikel, dus daar zal ik nu niet op ingaan.
Go taal echo server aanmaken
Laten we nu een eenvoudige Go-taal server toevoegen.
Maak eerst een workspace aan met go work init
in de oplossingsmap.
Voor .NET-ontwikkelaars is een Go workspace vergelijkbaar met een oplossing.
Maak vervolgens een map met de naam EchoServer aan, ga naar binnen en voer go mod init EchoServer
uit.
Met deze opdracht wordt een Go module aangemaakt. Een module is voor .NET-ontwikkelaars vergelijkbaar met een project.
Maak vervolgens een main.go
bestand en schrijf het volgende:
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}
Deze server leest de PORT
omgevingsvariabele die wordt geïnjecteerd wanneer Aspire AppHost wordt uitgevoerd om naar een bepaalde poort te luisteren, en voert de server uit.
Het is een eenvoudige server die een name
query accepteert en Hello, {name}
terugstuurt.
Laten we deze server nu toevoegen aan dotnet aspire.
De echo server toevoegen aan aspire
Ga terug naar het Aspire AppHost project waar je Valkey hebt toegevoegd en voeg community hosting voor de Go taal toe.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
Open vervolgens het Program.cs bestand en voeg de volgende instructie toe.
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();
Hier is echoServer
een implementatie van de IResourceBuilder
interface met informatie over het bouwen van de Go taal server.
De AddGolangApp
methode die we zojuist hebben toegevoegd is een extensiemethode van een custom host voor het toevoegen van Go-taal servers.
Het is te zien dat het vast de poort 3000 gebruikt en de PORT
omgevingsvariabele injecteert.
Ten slotte maakt WithExternalHttpEndpoints
externe toegang mogelijk.
Voor een test, probeer naar http://localhost:3000/?name=world
te gaan, en je zult Hello, world
zien verschijnen.
Maar momenteel heeft dotnet aspire een zware boete voor niet-.NET-projecten. Dat is...
Projectuitbreiding
Hoe zit het met horizontale schaalbaarheid?
Momenteel biedt dotnet aspire de WithReplica
optie alleen aan bouwers van .NET-projecten die zijn toegevoegd met de AddProject
methode.
Deze optie is echter niet beschikbaar voor Go taal hosts of externe projecten zoals AddContainer
.
Daarom moet je dit handmatig implementeren met behulp van een afzonderlijke load balancer of reverse proxy.
Als je dat doet, kan de reverse proxy echter een SPOF worden, dus het is een goed idee om de reverse proxy de WithReplica
optie te laten bieden.
Dan is het noodzakelijk dat de reverse proxy een .NET-project is.
Tot nu toe heb ik methoden zoals nginx, trafik en directe implementatie gebruikt om dit probleem aan te pakken, maar ik had geen directe oplossing toen de beperking van een .NET-project werd opgelegd. Daarom heb ik gezocht naar een reverse proxy die in .NET is geïmplementeerd, en gelukkig was er de optie van YARP. YARP is een reverse proxy die is geïmplementeerd in .NET, kan de rol van load balancer vervullen en biedt verschillende functies, dus ik vond dit een goede keuze.
Laten we nu YARP toevoegen.
Reverse proxy configureren met YARP
Maak eerst een project aan om YARP te gebruiken.
1dotnet new web -n ReverseProxy
Ga vervolgens naar het project en installeer YARP.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
Als de installatie voltooid is, open je het Program.cs bestand en schrijf je het volgende:
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"}");
Deze code is de basiscode voor het gebruik van YARP.routes
bevat de routeringsinformatie die de reverse proxy zal gebruiken, en clusters
bevat de clusterinformatie die de reverse proxy zal gebruiken.
Deze informatie wordt in de reverse proxy geladen met behulp van de LoadFromMemory
methode.
Ten slotte wordt de reverse proxy in kaart gebracht en uitgevoerd met behulp van de MapReverseProxy
methode.
Voor praktisch gebruik verwijzen we naar het reverse proxy project in het aspire apphost project en voegen we de volgende instructie toe en passen we het Program.cs bestand aan:
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 de reverse proxy naar de echo server verwijzen. De structuur is gewijzigd, zodat verzoeken van buitenaf worden ontvangen door de reverse proxy en worden doorgegeven aan de echo server.
Reverse proxy aanpassen
Eerst moeten we het listening adres van het project dat aan de reverse proxy is toegewezen, wijzigen.
Verwijder applicationUrl
in het Properties/launchSettings.json
bestand.
Open vervolgens het Program.cs bestand en pas het als volgt aan:
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"}");
We wijzigen eerst de informatie voor routes
en clusters
.
We voegen respectievelijk echo-route
en echo-cluster
toe om verzoeken naar de echo server te sturen.
We passen het ook zo aan dat het adres van de echo server wordt gelezen van een omgevingsvariabele.
De regel voor dit adres is services__{service-name}__http__{index}
.
In het geval van de echo server is de servicenaam echo-server
en is er één instantie, dus we gebruiken 0
als index.
Als je een asp .net core server toevoegt, kunnen er meerdere instanties worden aangemaakt via WithReplica
, dus je kunt de index verhogen om ze te gebruiken.
De uitzonderingsverwerking http://localhost:8080
is een onzinwaarde zonder enige betekenis.
Als je het project nu uitvoert en naar http://localhost:3000/?name=world
gaat, zul je nog steeds Hello, world
zien verschijnen.
Uitbreidingsideeën
Nu hebben we gecontroleerd dat we een Go server aan dotnet aspire kunnen toevoegen en verzoeken kunnen doorgeven via een reverse proxy. We kunnen dit proces nu uitbreiden om het programmatisch te implementeren. We kunnen bijvoorbeeld een nummering toevoegen na de servicenaam voor de echo server om meerdere instanties te creëren en automatisch instellingen voor de reverse proxy toevoegen.
Pas het Program.cs bestand in het aspire apphost project aan om de reverse proxy en echo server als volgt te gebruiken:
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}
Pas vervolgens het Program.cs bestand van het reverse proxy project als volgt aan:
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};
Voeg bestemming instellingen toe voor de 8 echo server instanties die zijn toegevoegd.
De reverse proxy heeft nu bestemmingsinformatie voor de toegenomen echo servers en kan verzoeken doorgeven.
Als je naar http://localhost:3000/?name=world
gaat, zul je nog steeds Hello, world
zien verschijnen.
Tot slot
In dit artikel heb ik het proces uitgelegd van het toevoegen van een Go server aan dotnet aspire en het doorgeven van verzoeken via een reverse proxy. De uitbreiding is echter nog niet volledig geschreven, en ik heb een voorbeeld dat meer programmatisch kan worden geïmplementeerd via omgevingsvariabelen in een aparte repo. Raadpleeg snowmerak/AspireStartPack voor gedetailleerde projectconfiguratie en code.
Ik verwacht persoonlijk dat dotnet aspire zijn eigen rol kan vervullen als alternatief voor docker compose en als een tool voor cloud implementatie. Er zijn al generatoren die docker compose of k8s manifesten genereren, waardoor de toegankelijkheid van infrastructuur tools voor reguliere ontwikkelaars is verbeterd, denk ik.