API latency plays a crucial role in performance and user experience. High latency frustrates users, reduces scalability, and increases infrastructure costs. This guide dives deep into causes, measurement, and optimization strategies for C# .NET APIs.
1. Understanding API Latency
API latency is the total time from a client sending a request to receiving a response — covering network transmission, server-side processing, and database interactions.
Types of latency:
- Network latency — distance, bandwidth, congestion. Fix: CDNs.
- Processing latency — inefficient code, blocking operations. Fix: async programming.
- Database latency — slow queries, missing indexes. Fix: query optimization, caching, connection pooling.
Client → (Network) → API Gateway → (Processing) → Database → (DB latency) → API Gateway → Client
Enter fullscreen mode Exit fullscreen mode
2. Measuring API Latency
Stopwatch — quick and surgical:
var stopwatch = Stopwatch.StartNew();
await ProcessRequestAsync();
stopwatch.Stop();
Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} ms");
Enter fullscreen mode Exit fullscreen mode
Middleware timing — covers every request:
public class LatencyMiddleware
{
private readonly RequestDelegate _next;
public LatencyMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
await _next(context);
sw.Stop();
Console.WriteLine($"Request latency: {sw.ElapsedMilliseconds} ms");
}
}
Enter fullscreen mode Exit fullscreen mode
Register in Program.cs:
app.UseMiddleware<LatencyMiddleware>();
Enter fullscreen mode Exit fullscreen mode
Application Insights — production-grade:
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry(
Configuration["ApplicationInsights:InstrumentationKey"]);
}
Enter fullscreen mode Exit fullscreen mode
Other tools: Postman (endpoint testing), JMeter (load testing), Grafana + Prometheus (dashboards), Jaeger / OpenTelemetry (distributed tracing).
3. Latency in C# .NET — How Requests Flow
Client → Web Server (Kestrel/IIS) → Middleware Pipeline → Controller → DB/Logic → Middleware → Response
Enter fullscreen mode Exit fullscreen mode
Synchronous vs asynchronous — the single biggest lever:
// ❌ Synchronous — blocks the thread
public IActionResult GetData()
{
var data = _service.GetData();
return Ok(data);
}
// ✅ Asynchronous — frees the thread for other requests
public async Task<IActionResult> GetDataAsync()
{
var data = await _service.GetDataAsync();
return Ok(data);
}
Enter fullscreen mode Exit fullscreen mode
Common bottlenecks:
Bottleneck Fix Blocking I/Oasync/await throughout
Slow DB queries
Indexes, AsNoTracking() for reads
Heavy middleware
Remove unnecessary steps, async logging
Large serialization
System.Text.Json, smaller payloads
4. Optimizing API Latency — Best Practices
Async/Await
public async Task<IActionResult> GetUserDataAsync()
{
var data = await _databaseService.GetUserDataAsync();
return Ok(data);
}
Enter fullscreen mode Exit fullscreen mode
In-Memory Caching
public async Task<User> GetUserByIdAsync(int userId)
{
if (!_memoryCache.TryGetValue(userId, out User user))
{
user = await _databaseService.GetUserByIdAsync(userId);
_memoryCache.Set(userId, user, TimeSpan.FromMinutes(10));
}
return user;
}
Enter fullscreen mode Exit fullscreen mode
Response Compression
public void Configure(IApplicationBuilder app)
{
app.UseResponseCompression();
}
Enter fullscreen mode Exit fullscreen mode
Efficient Serialization
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
[HttpGet]
public IActionResult GetUser(int userId)
{
var user = _databaseService.GetUserById(userId);
return Content(JsonSerializer.Serialize(user, _jsonOptions), "application/json");
}
Enter fullscreen mode Exit fullscreen mode
5. Advanced Techniques
Message Queues — offload non-urgent work
public async Task<IActionResult> ProcessOrderAsync(Order order)
{
await _messageQueue.SendAsync(order);
return Accepted(); // responds immediately
}
Enter fullscreen mode Exit fullscreen mode
Distributed Caching with Redis
public async Task<User> GetUserByIdAsync(int userId)
{
var cache = _redis.GetDatabase();
var cached = await cache.StringGetAsync(userId.ToString());
if (!cached.IsNullOrEmpty)
return JsonSerializer.Deserialize<User>(cached);
var user = await _databaseService.GetUserByIdAsync(userId);
await cache.StringSetAsync(
userId.ToString(),
JsonSerializer.Serialize(user),
TimeSpan.FromMinutes(10));
return user;
}
Enter fullscreen mode Exit fullscreen mode
Database Sharding & HTTP/2
For high-volume systems, sharding distributes data across DB instances. Upgrading to HTTP/2 or HTTP/3 enables multiplexing — multiple requests over a single connection, reducing handshake overhead.
6. Case Study: E-Commerce Checkout API
Problem: Checkout averaging 2–3 seconds. Identified via Application Insights + SQL Profiler:
- Synchronous inventory service calls
- Missing indexes on the
orderstable
Fixes applied:
- Refactored inventory check to
async/await - Added indexes on
orderstable, optimized inventory queries - Redis caching for inventory data that rarely changes
Result: Average checkout response dropped from 3 seconds → under 500ms.
7. Monitoring in Production
- Azure Monitor — API performance and resource utilization
- Prometheus + Grafana — real-time metrics and dashboards
- New Relic — end-to-end latency per endpoint
- Serilog — structured async logging
Set up alerts for latency thresholds so the team can respond to regressions before users notice.
Conclusion
Reducing API latency is not a one-time task. The core levers are: async programming, database optimization, caching (in-memory and distributed), payload compression, and middleware hygiene. Pair those with ongoing monitoring and load testing, and your .NET API stays fast as traffic scales.
Originally published on Medium.
답글 남기기