I moderne softwareudvikling er effektiv ressourcestyring og optimering af ydeevne essentielle faktorer for at opnå hurtige og pålidelige applikationer. En vigtig del af denne optimering er at forstå, hvordan man måler og reducerer hukommelsesforbrug i applikationer, især når det gælder håndtering af store mængder data. I dette afsnit dykker vi ned i, hvordan man kan bruge .NET's indbyggede funktionaliteter til at måle ydeevne og hukommelsesforbrug, samt hvad man bør overveje, når man arbejder med ressourcer i et program.

En af de grundlæggende metoder til at overvåge ydeevne i .NET-applikationer er at bruge Stopwatch-klassen sammen med processrelaterede oplysninger om hukommelsesforbrug. I eksemplet, som er illustreret i starten af denne artikel, viser vi, hvordan man opretter en simpel "Recorder"-klasse til at registrere hukommelsesforbrug før og efter en operation. For at sikre, at vi får målt det rigtige hukommelsesforbrug, bruges en teknik, der tvangsmæssigt kører garbage collection (GC) før målingerne. Dette er et avanceret trick, som sjældent bør anvendes i almindelig applikationskode, da garbage collection i sig selv er optimeret til at håndtere hukommelsen bedre end programmøren selv kunne.

I eksemplet starter vi ved at kalde GC.Collect() flere gange for at sikre, at al ubrugte hukommelse bliver ryddet op, før vi begynder at måle hukommelsesforbruget. Denne form for kontrol er undtagelsen snarere end reglen. Garbage collector i .NET er designet til at administrere hukommelsen autonomt, og i de fleste tilfælde vil den træffe bedre beslutninger om, hvornår det er nødvendigt at køre en oprydning.

Når koden kører, skaber vi en stor array af 10.000 heltal og måler derefter, hvor meget fysisk og virtuel hukommelse der bruges under operationen. I eksemplet kan vi se, hvordan maskinens hardwarekonfiguration påvirker målingerne – f.eks. er den fysiske hukommelse på en Mac mini M1 lavere end på en anden maskine, men den virtuelle hukommelse er højere. Dette demonstrerer, hvordan forskellige systemer kan håndtere hukommelse forskelligt, selv med samme kode.

Et vigtigt aspekt ved ydeevneoptimering i .NET er valg af datatyper og metoder. Et godt eksempel på dette er, hvordan vi arbejder med tekstbehandling. Når vi skal sammenkæde strenge, er der stor forskel på, hvordan StringBuilder og den almindelige + operator fungerer. Strenge i .NET er immutabel, hvilket betyder, at hver gang en streng ændres, skabes en ny instans. Dette kan føre til betydeligt hukommelsesforbrug og nedsat ydeevne, hvis der udføres mange ændringer i en løkke. Brug af StringBuilder i stedet for at concatenere strenge med + operatoren kan give en betydelig forbedring i både hukommelsesforbrug og behandlingstid.

For eksempel, når vi sammenkæder 50.000 heltal, viser målingerne, at StringBuilder kun bruger omkring 1 MB fysisk hukommelse og er langt hurtigere end at bruge + operatoren. Dette skyldes, at StringBuilder arbejder med et enkelt buffer og tilføjer data til det, mens + operatoren konstant skaber nye strenge, hvilket er langt mere ressourcekrævende.

For at få endnu mere præcise målinger og benchmarks på ydeevnen i din kode, kan du benytte dig af eksterne værktøjer som Benchmark.NET, et populært bibliotek til performance benchmarking i .NET. Benchmark.NET giver udviklere en enkel og effektiv måde at måle præstationer på, og bruges ofte i Microsofts egne blogindlæg og præsentationer om ydeevneforbedringer. Dette værktøj gør det muligt at sammenligne metoder til string-manipulation, som vi har set i eksemplet med StringBuilder og string-konkatenering, samt mange andre aspekter af applikationens ydeevne.

En praktisk implementering med Benchmark.NET kunne være at definere to metoder, en til at bruge StringBuilder og en til at bruge den traditionelle + operator. Ved at definere disse som benchmarks og køre dem i et kontrolleret miljø, kan vi få præcise målinger af både tid og hukommelsesforbrug. Resultaterne af sådanne tests kan hjælpe udviklere med at vælge de mest effektive metoder i deres applikationer.

Når du arbejder med store mængder data, er det vigtigt ikke kun at fokusere på, hvad der virker hurtigt, men også på, hvordan ressourcer som hukommelse og CPU-tid bliver brugt. Overvågning af ydeevne og hukommelsesforbrug er en løbende proces, og det er vigtigt at kunne tilpasse din tilgang afhængigt af applikationens behov og det underliggende hardware.

En vigtig overvejelse, som mange udviklere overser, er hvordan optimering på et lille niveau kan have stor betydning i større applikationer eller systemer. For eksempel kan små forbedringer i hukommelsesforbrug og behandlingstid akkumuleres og have en stor indvirkning på den samlede ydeevne, især når applikationen kører på systemer med begrænsede ressourcer.

I sidste ende kræver effektivt arbejde med ydeevne og hukommelse en god forståelse af de værktøjer, du har til rådighed i .NET, samt en systematisk tilgang til at evaluere og optimere din kode baseret på konkrete målinger og data.

Hvordan man implementerer rate limiting og JWT-baseret autentificering i en web API

I moderne webtjenester er det vigtigt at sikre både ydeevne og sikkerhed. En væsentlig teknik til at beskytte en API mod overbelastning er rate limiting, mens autentificering er afgørende for at sikre, at kun autoriserede brugere kan få adgang til bestemte ressourcer. I denne sammenhæng vil vi gennemgå, hvordan man implementerer rate limiting og JWT-baseret autentificering i en minimal API-applikation ved hjælp af .NET.

I først omgang kan man implementere rate limiting i sin applikation ved at benytte sig af den indbyggede funktionalitet i ASP.NET Core. Rate limiting gør det muligt at kontrollere antallet af anmodninger, som en bruger kan sende til en API i et givet tidsvindue, hvilket forhindrer overbelastning af serveren og sikrer, at systemet forbliver responsivt, selv ved høj trafik. En af de mest anvendte metoder til dette formål er FixedWindowRateLimiter, som begrænser antallet af anmodninger baseret på et fast tidsvindue.

For at bruge rate limiting i en web API, skal du først importere de nødvendige namespaces for rate limiting i Program.cs-filen. Derefter kan du definere, om du vil anvende den indbyggede rate limiting funktionalitet. Hvis du ønsker at bruge den, skal du konfigurere en rate limiting-politik og anvende den på specifikke API-endpoints. Et eksempel på hvordan dette kan gøres, er ved at definere en politik, der tillader maksimalt 5 anmodninger per 10 sekunder.

For at definere en sådan politik, kan følgende kode anvendes:

csharp
RateLimiterOptions rateLimiterOptions = new(); rateLimiterOptions.AddFixedWindowLimiter( policyName: "fixed5per10seconds", options => { options.PermitLimit = 5; options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; options.QueueLimit = 2; options.Window = TimeSpan.FromSeconds(10); }); app.UseRateLimiter(rateLimiterOptions);

Denne konfiguration sikrer, at klienten kun kan sende 5 anmodninger hver 10 sekunder. Efter at have overskredet denne grænse, vil klienten blive afvist, indtil tidsvinduet fornyes.

Derudover, for at teste og interagere med API'en, kan man benytte en klientapplikation, som genererer en GUID-baseret klient-ID. Denne klient kan derefter sende anmodninger til serveren, hvor antallet af anmodninger og den nødvendige ventetid mellem dem bliver tydeligt.

En anden vigtig komponent i moderne web API-sikkerhed er autentificering og autorisation. Her anvendes ofte JSON Web Tokens (JWT), som giver en sikker metode til at transmittere brugerens identitet og adgangsrettigheder som et JSON-objekt. JWT'er består af tre dele: header, payload og signature, som alle er Base64-kodede. Det bruges oftest i scenarier, hvor en bruger logger ind på et system og derefter får tildelt et JWT, som sendes med hver efterfølgende anmodning til serveren.

For at implementere JWT-baseret autentificering i en minimal API i .NET, skal du først tilføje de nødvendige pakker og konfigurationer. I Program.cs-filen skal du tilføje middleware til både autentificering og autorisation. Det kan gøres ved at tilføje følgende konfiguration:

csharp
builder.Services.AddAuthorization(); builder.Services.AddAuthentication("Bearer") .AddJwtBearer();

Herefter kan du definere, at alle anmodninger skal autentificeres via JWT. Dette sikrer, at kun brugere med et gyldigt token kan få adgang til beskyttede ressourcer. For at teste autentificeringen kan man bruge dotnet user-jwts kommandolinjeværktøjet til at generere og administrere JWT'er lokalt under udvikling.

En grundlæggende forståelse af, hvordan JWT fungerer, er afgørende for at kunne implementere en sikker API. JWT'er er designet til at være kompakte, hvilket gør dem ideelle til at blive sendt som HTTP-header. Når en bruger logger ind, udstedes et JWT, og derefter bruges dette token til at bekræfte brugerens identitet ved hver anmodning.

For at sikre din API mod uautoriseret adgang, skal du anvende denne tokenbaserede autentificering og være opmærksom på de sikkerhedsmæssige foranstaltninger, der følger med JWT. Det er vigtigt at beskytte den private nøgle, som bruges til at signere JWT'er, og sørge for at anvende sikre forbindelser (f.eks. HTTPS), når token sendes over nettet.

Det er vigtigt at huske, at rate limiting og JWT-baseret autentificering ikke kun er sikkerhedsforanstaltninger, men også forbedrer API'ens ydeevne og brugeroplevelse. Rate limiting beskytter din server mod misbrug og forhindrer, at en enkelt klient overbelaster systemet, mens JWT giver en effektiv og sikker måde at autentificere brugere på tværs af forskellige applikationer og platforme.

For at udnytte disse teknologier fuldt ud, bør udviklere forstå, hvordan man kombinerer dem i et fuldt funktionelt API, der både er hurtigt og sikkert. Det er også nødvendigt at have en god forståelse af, hvordan autentificering og rate limiting interagerer, især når du arbejder med et system, der skal håndtere mange samtidige brugere og anmodninger.