Arash Zand

API 延迟在性能和用户体验中起着至关重要的作用。高延迟会让用户感到沮丧,降低可扩展性并增加基础设施成本。本指南深入探讨了 C# .NET API 的成因、测量方法和优化策略。


1. 理解 API 延迟

API 延迟是指从客户端发送请求到收到响应的总时间——包括网络传输、服务器端处理和数据库交互。

延迟的类型:

  • 网络延迟 — 距离、带宽、拥塞。解决方法:CDN。
  • 处理延迟 — 代码效率低下、阻塞操作。解决方法:异步编程。
  • 数据库延迟 — 查询缓慢、缺少索引。解决方法:查询优化、缓存、连接池。
Client → (Network) → API Gateway → (Processing) → Database → (DB latency) → API Gateway → Client

Enter fullscreen mode Exit fullscreen mode


2. 测量 API 延迟

Stopwatch —— 快速而精准:

var stopwatch = Stopwatch.StartNew();
await ProcessRequestAsync();
stopwatch.Stop();
Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} ms");

Enter fullscreen mode Exit fullscreen mode

中间件计时 —— 覆盖每个请求:

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

Program.cs 中注册:

app.UseMiddleware<LatencyMiddleware>();

Enter fullscreen mode Exit fullscreen mode

Application Insights —— 生产级:

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(
        Configuration["ApplicationInsights:InstrumentationKey"]);
}

Enter fullscreen mode Exit fullscreen mode

其他工具: Postman(端点测试)、JMeter(负载测试)、Grafana + Prometheus(仪表盘)、Jaeger / OpenTelemetry(分布式追踪)。


3. C# .NET 中的延迟——请求流程

Client → Web Server (Kestrel/IIS) → Middleware Pipeline → Controller → DB/Logic → Middleware → Response

Enter fullscreen mode Exit fullscreen mode

同步 vs 异步 —— 最大杠杆:

// ❌ 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

常见瓶颈:

瓶颈 修复方案
阻塞 I/O async/await 贯穿始终
慢查询 索引、读取时使用 AsNoTracking()
重量级中间件 移除不必要的步骤、异步日志
大型序列化 System.Text.Json、更小的负载

4. 优化 API 延迟——最佳实践

Async/Await

public async Task<IActionResult> GetUserDataAsync()
{
    var data = await _databaseService.GetUserDataAsync();
    return Ok(data);
}

Enter fullscreen mode Exit fullscreen mode

内存缓存

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

响应压缩

public void Configure(IApplicationBuilder app)
{
    app.UseResponseCompression();
}

Enter fullscreen mode Exit fullscreen mode

高效序列化

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. 进阶技术

消息队列——卸载非紧急工作

public async Task<IActionResult> ProcessOrderAsync(Order order)
{
    await _messageQueue.SendAsync(order);
    return Accepted(); // responds immediately
}

Enter fullscreen mode Exit fullscreen mode

使用 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

数据库分片 & HTTP/2

对于高流量系统,分片将数据分布在多个数据库实例上。升级到 HTTP/2 或 HTTP/3 可实现多路复用——在单个连接上处理多个请求,从而减少握手开销。


6. 案例研究:电商结账 API

问题:结账平均耗时 2–3 秒。通过 Application Insights + SQL Profiler 发现:

  • 同步库存服务调用
  • orders 表缺少索引

采取的修复措施:

  1. 将库存检查重构为 async/await
  2. orders 表上添加索引,优化库存查询
  3. 对很少变化的库存数据使用 Redis 缓存

结果:平均结账响应从 3 秒降至 500 毫秒以下


7. 生产环境监控

  • Azure Monitor — API 性能和资源利用率
  • Prometheus + Grafana — 实时指标和仪表盘
  • New Relic — 每个端点的端到端延迟
  • Serilog — 结构化异步日志记录

为延迟阈值设置警报,以便团队在用户察觉到问题前响应回归。


结论

降低 API 延迟不是一次性任务。核心手段包括:异步编程、数据库优化、缓存(内存和分布式)、负载压缩以及中间件优化。结合持续监控和负载测试,您的 .NET API 将在流量增长时保持快速响应。


最初发布于 Medium