.Net APIs : Amélioration des performances
Dans le monde du développement des applications modernes, les APIs performantes sont obligatoires pour garantir la responsivité, la stabilité et la rapidité d’exécution des requêtes.
Qu’il s’agisse de la création d’un nouveau web service ou de l’optimisation d’un service existant, il existe quelques règles à suivre en .Net pour améliorer de façon significative les performances d’une API.
1 – Utilisation d’async/await
La programmation asynchrone empêche votre application de bloquer les threads en attendant la fin des opérations d’Entée/Sortie (Lecture des fichiers, Attente des appels vers une base de données, attente des appels vers une API, etc).
En utilisant async/await, .NET peut traiter plus de requêtes simultanément sans bloquer les threads. Cela augmente l’évolutivité.
Préférez utiliser des méthodes asynchrones plutôt que synchrones :
[HttpGet("items")]
public async Task<IActionResult> GetItemsAsync()
{
var items = await _itemService.GetItemsAsync();
return Ok(items);
}2 – Utilisation de AsNoTracking()
Pour ceux qui comme moi sont aujourd’hui habitués à travailler avec le package EF Core, l’utilisation de AsNoTracking() dans les requêtes de lecture de données permet d’améliorer la rapidité d’exécution.
Pour information, AsNoTracking() permet de dire à votre DBContext d’éviter de « tracker » les entités, ce qui est utile dans les cas des requêtes de lecture, où on n’aura pas besoin de modification/suppression de données.
public async Task<List<User>> GetUsersAsync()
{
return await _dbContext.Users
.AsNoTracking()
.Where(u => u.IsActive)
.ToListAsync();
}3 – Ajout du Cache
La mise en cache est l’une des optimisations les plus importantes que vous puissiez appliquer.
Le stockage des données fréquemment consultées en mémoire ou l’utilisation d’un cache distribué (comme Redis) réduit les allers-retours dans la base de données.
Plusieurs types de Caches existent en .Net:
- OutputCache
- IDistributedCache
- HybridCache (.NET 9)
- Fusion Cache
Nous nous interesseront dans cet article à OutputCache (qui est la mise en place du Cache la plus courante), et HybridCache (qui est une nouveauté de .Net 9 à découvrir).
Le Output Caching stocke l’intégralité de la réponse HTTP pendant une durée définie, la renvoyant directement sans réexécuter le contrôleur API.
builder.Services.AddOutputCache();
var app = builder.Build();
app.UseOutputCache();
[HttpGet("products")]
[OutputCache(Duration = 60)] // Durée avant re-exécution de la requete
public async Task<IActionResult> GetProductsAsync()
{
var products = await _productService.GetProductsAsync();
return Ok(products);
}Il s’agit du type de mise en cache le plus simple à ajouter à votre application car il ne nécessite presque aucun code.
Si vous avez besoin d’une gestion plus avancée de la mise en place du cache dans votre application, je vous recommande de jeter un oeil sur les HybridCache, nouveauté de .Net 9.
Voici un exemple de l’utilisation de HybridCache sur une requête :
builder.Services.AddHybridCache();
[HttpGet("orders/{id}")]
public async Task<IActionResult> GetOrderAsync(int id,
[FromServices] IHybridCache cache)
{
string cacheKey = $"Order_{id}";
var order = await cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
using var context = new AppDbContext();
return await context.Orders.FindAsync(id);
});
if (order is null)
{
return NotFound();
}
return Ok(order);
}4 – Activer la compression des réponses
Compresser vos réponses avec Brotli ou GZIP peut réduire considérablement la taille de la charge utile (payload).
Des réponses plus petites signifient un transfert de données plus rapide et une meilleure expérience utilisateur.
Brotli et Gzip réduisent la taille des données sortantes des fichiers JSON, HTML ou statiques.
Les ajouter en premier dans votre program.cs garantira des charges utiles plus petites.
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Fastest;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Fastest;
});
var app = builder.Build();
app.UseResponseCompression();5 – Optimiser l’ordre des middlewares dans Program.cs
L’ordre des middlewares affecte les performances.
Certains middlewares doivent s’exécuter le plus tôt possible, tandis que d’autres (comme le routage, l’authentification et l’autorisation) doivent suivre une séquence logique.
Si vous utilisez la compression des réponses, assurez vous de l’ajouter avant tous les autres (comme vu plus haut), afin d’obtenir des charges utiles plus petites :
var app = builder.Build();
// Bon ordre
app.UseResponseCompression(); // En premier
app.UseStaticFiles(); // Static files
app.UseRouting();
app.UseAuthentication(); // Authentication avant authorization
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});6 – Activer les protocoles HTTP/2 et HTTP/3
HTTP/2 et HTTP/3 offrent des améliorations significatives des performances par rapport à HTTP/1, notamment le multiplexage et une latence réduite.
Configurer Kestrel pour prendre en charge plusieurs protocoles est simple :
// In Program.cs
builder.WebHost.ConfigureKestrel(options =>
{
// HTTP/1.1
options.ListenAnyIP(5000);
// HTTPS with HTTP/1.1, HTTP/2 and HTTP/3
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
});
});
var app = builder.Build();
// Rest of the configurationLes clients qui prennent en charge les protocoles les plus récents les utiliseront automatiquement, tandis que les clients plus anciens pourront continuer à utiliser HTTP/1.1.
7 – Implémenter les Rate Limit et Throttling
Le Rate Limit contrôle l’utilisation des ressources et empêche les comportements abusifs.
En limitant le nombre de requêtes dans une certaine fenêtre de temps, vous protégez votre API des charges excessives ou des attaques DDoS.
En outre, le Rate Limit peut aussi être mis en œuvre pour gérer un certain nombre de demandes en fonction de l’abonnement de l’utilisateur et restreindre les demandes supplémentaires.
C’est ainsi que fonctionne ChatGPT, par exemple.
Voici comment implémenter le Rate Limit avec les fonctionnalités intégrées dans ASP.NET Core :
// In Program.cs
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
{
var ipAddress = context.Connection.RemoteIpAddress;
return RateLimitPartition.GetFixedWindowLimiter(ipAddress,
_ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100, // Autorise 100 requetes
Window = TimeSpan.FromMinutes(1), // sur une fenetre de 1 minute
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 0
});
});
});
var app = builder.Build();
app.UseRateLimiter();Cet exemple utilise le Rate Limit des APIs .NET pour limiter chaque IP à 100 requêtes par minute.
8 – Remplacer les Controllers par les minimal APIs
Les minimal APIs suppriment une partie de la surcharge du pipeline MVC traditionnel.
Les minimal APIs dans .NET 9 ont bénéficié d’une énorme amélioration des performances et peuvent traiter 15% de requêtes en plus par seconde que dans .NET 8.
De plus, les minimal APIs consomment 93% de mémoire en moins par rapport à une version précédente.
Ils sont concis, rapides et prennent en charge l’injection de dépendances, les filtres et d’autres fonctionnalités courantes d’ASP.NET Core :
var app = builder.Build();
// Minimal API endpoint
app.MapGet("/customers/{id}", async (int id) =>
{
var customer = await _dbContext.Customers.FindAsync(id);
return customer is not null
? Results.Ok(customer)
: Results.NotFound();
});
app.Run();Résumé
Considérez les techniques suivantes pour augmenter considérablement les performances de vos API et l’expérience utilisateur :
- Utilisation de Async/Await
- Optimisation des requêtes en base de données (EF Core)
- Mise en place du cache
- Compression des réponses
- Ordonnément des middlewares
- Activation de HTTP/2 et HTTP/3
- Mise en place du Rate Limit et du Throttling
- Minimal APIs (.Net 9)
Adoptez ces conseils progressivement, mesurez les améliorations à l’aide d’outils tels que OpenTelemetry ou BenchmarkDotnet et affinez continuellement vos APIs .NET pour obtenir les meilleurs résultats.
Laisser un commentaire