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

一般的なボトルネック:

Bottleneck Fix
Blocking I/O async/await throughout
Slow DB queries Indexes, AsNoTracking() for reads
Heavy middleware Remove unnecessary steps, async logging
Large serialization System.Text.Json, smaller payloads

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

高ボリュームシステムでは、シャーディングによって複数の DB インスタンスにデータを分散します。HTTP/2 または HTTP/3 へのアップグレードにより、多重化が可能になり、単一のコネクション上で複数のリクエストを処理できるようになり、ハンドシェイクのオーバーヘッドを削減します。


6. ケーススタディ: Eコマースのチェックアウト API

問題: チェックアウトに平均 2–3 秒かかっていた。Application Insights + SQL Profiler で特定:

  • 同期的な在庫サービス呼び出し
  • orders テーブルにインデックスがない

適用した修正:

  1. 在庫チェックを async/await にリファクタリング
  2. orders テーブルにインデックスを追加、在庫クエリを最適化
  3. 変更が少ない在庫データに対して Redis キャッシングを適用

結果: チェックアウトの平均レスポンスが 3 秒 → 500ms 未満 に短縮。


7. 本番環境でのモニタリング

  • Azure Monitor — API パフォーマンスとリソース利用状況
  • Prometheus + Grafana — リアルタイムメトリクスとダッシュボード
  • New Relic — エンドポイントごとのエンドツーエンドレイテンシ
  • Serilog — 構造化された非同期ログ

レイテンシの閾値に対するアラートを設定することで、ユーザーが気づく前にチームが回帰に対処できます。


結論

API レイテンシの削減は一度きりの作業ではありません。核心となる要素は、非同期プログラミング、データベースの最適化、キャッシング(インメモリおよび分散)、ペイロード圧縮、ミドルウェアの健全性です。これらを継続的なモニタリングと負荷テストと組み合わせることで、.NET API はトラフィックが増大しても高速な状態を維持できます。


Originally published on Medium.