首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

8 大提升 ASP.NET Web API 性能的核心技術實戰總結

     編輯:ASP.NET 基礎

前言

Web API 性能直接決定用戶體驗與系統吞吐上限。本文總結 8 項經實戰驗證的性能優化技術,涵蓋序列化、數據訪問、協議、壓縮、緩存、異步等核心維度。

1. 採用最高性能 JSON 序列化庫

庫相對性能特點
System.Text.Json (.NET Core 內置)基準 1.0x零依賴、高性能、原生支援
Jil~1.2-1.5x極致性能、嚴格標準、StackOverflow 採用
Utf8Json~1.1-1.3x零分配、流式、Unity 採用
Newtonsoft.Json~0.6-0.8x功能最全、兼容性最好、生態成熟
ServiceStack.Text~0.9-1.1x早期高性能代表、現已非首選

遷移示例

// Startup.cs - System.Text.Json (推薦 .NET Core 3.1+)
services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    });

// 或 Jil (極致性能場景)
services.AddControllers()
    .AddJil(options => 
    {
        options.DateFormat = "yyyy-MM-dd HH:mm:ss";
        options.IncludeInherited = true;
    });

2. 手動從 DataReader 序列化 JSON (避免中間對象)

[HttpGet]
public HttpResponseMessage GetLargeDataset()
{
    using (var conn = new SqlConnection(_connStr))
    using (var cmd = new SqlCommand("SELECT * FROM LargeTable", conn))
    {
        conn.Open();
        using (var reader = cmd.ExecuteReader())
        {
            var sb = new StringBuilder();
            sb.Append('[');
            bool first = true;
            
            while (reader.Read())
            {
                if (!first) sb.Append(',');
                first = false;
                
                sb.Append('{');
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    if (i > 0) sb.Append(',');
                    string name = reader.GetName(i);
                    object value = reader.GetValue(i);
                    sb.Append($"\"{name}\":{SerializeValue(value)}");
                }
                sb.Append('}');
            }
            sb.Append(']');
            
            var response = Request.CreateResponse(HttpStatusCode.OK);
            response.Content = new StringContent(sb.ToString(), Encoding.UTF8, "application/json");
            return response;
        }
    }
}

private string SerializeValue(object value)
{
    if (value == null || value == DBNull.Value) return "null";
    if (value is string) return $"\"{value.ToString().EscapeJson()}\"";
    if (value is bool) return (bool)value ? "true" : "false";
    if (value is DateTime) return $"\"{((DateTime)value):yyyy-MM-ddTHH:mm:ss}\"";
    return value.ToString(); // 數字類型
}

效果:省去 List<T> 分配 + 序列化雙重開銷,大數據集性能提升 3-5 倍。

3. 採用高效二進制協議

協議體積壓縮比序列化速度適用場景
Protocol BuffersJSON 的 30-50%極快內部微服務、高頻調用
MessagePackJSON 的 50-70%極快跨語言、類 JSON 結構
FlatBuffers零拷貝解析極快遊戲、實時系統
JSON基準基準公共 API、調試友好

Protobuf 示例

// user.proto
syntax = "proto3";
message User {
  int64 id = 1;
  string name = 2;
  string email = 3;
  repeated string roles = 4;
}
// 服務端
[HttpGet("{id}")]
public async Task GetUserProto(long id)
{
    var user = await _repo.GetUserAsync(id);
    var proto = user.ToProto(); // 映射
    return File(proto.ToByteArray(), "application/x-protobuf");
}

// 客戶端
var response = await httpClient.GetAsync("/api/user/123?format=proto");
var bytes = await response.Content.ReadAsByteArrayAsync();
var user = User.Parser.ParseFrom(bytes);

4. 啟用響應壓縮

// Startup.cs - ASP.NET Core
services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add();
    options.Providers.Add(); // 更高壓縮比
    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] 
    { "application/json", "application/x-protobuf" });
});

services.Configure(options => options.Level = CompressionLevel.Fastest);
services.Configure(options => options.Level = CompressionLevel.Fastest);

// 中間件順序重要:在 UseRouting 之前
app.UseResponseCompression();

效果:JSON 響應體積減少 70-90%,傳輸時間顯著降低。

5. 多層緩存策略

層級技術TTL場景
HTTP 緩存ETag / Cache-Control30s-5m公開只讀數據
輸出緩存ResponseCache / OutputCache1-60m計算密集型端點
分佈式緩存Redis / NCache5m-24h熱點數據、會話
本地緩存MemoryCache / Caffeine1-10m極熱配置、字典表

Redis 緩存裝飾器模式

public class CachedUserRepository : IUserRepository
{
    private readonly IUserRepository _inner;
    private readonly IDistributedCache _cache;
    
    public async Task GetByIdAsync(long id)
    {
        string key = $"user:{id}";
        var cached = await _cache.GetStringAsync(key);
        if (cached != null) return JsonSerializer.Deserialize(cached);
        
        var user = await _inner.GetByIdAsync(id);
        if (user != null)
            await _cache.SetStringAsync(key, JsonSerializer.Serialize(user), 
                new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) });
        return user;
    }
}

6. 關鍵路徑使用原生 ADO.NET / Dapper

ORM 便利性以性能為代價,核心高頻接口建議:

  • Dapper:微型 ORM,性能接近原生 ADO.NET
  • 手寫 ADO.NET:極致控制,配合 `SqlDataReader` 逐行處理
  • 避免 EF Core 追蹤:`AsNoTracking()`、`AsSplitQuery()`

性能對比參考

| Method              | Mean (ms) | Allocated (KB) |
|---------------------|-----------|----------------|
| HandCoded ADO.NET   | 1.2       | 45             |
| Dapper              | 1.5       | 52             |
| EF Core (NoTracking)| 3.8       | 180            |
| EF Core (Tracking)  | 8.5       | 420            |

7. 全鏈路異步編程

// 控制器
[HttpGet]
public async Task> GetUserAsync(long id)
{
    // 異步鏈路:Controller -> Service -> Repository -> DB
    var user = await _userService.GetByIdAsync(id);
    return user != null ? Ok(user) : NotFound();
}

// Service
public async Task GetByIdAsync(long id)
{
    // 並行執行獨立查詢
    var userTask = _repo.GetUserAsync(id);
    var rolesTask = _roleRepo.GetUserRolesAsync(id);
    
    await Task.WhenAll(userTask, rolesTask);
    
    var user = userTask.Result;
    if (user != null) user.Roles = rolesTask.Result;
    return user;
}

// Repository - 真正異步 IO
public async Task GetUserAsync(long id)
{
    using (var conn = new SqlConnection(_connStr))
    {
        await conn.OpenAsync();
        return await conn.QueryFirstOrDefaultAsync(
            "SELECT * FROM Users WHERE Id = @id", new { id });
    }
}

關鍵:`async`/`await` 全鏈路貫通,避免同步阻塞線程池,吞吐量提升 5-10 倍。

8. 聚合響應減少請求次數

// 聚合 DTO
public class DashboardData
{
    public UserProfile Profile { get; set; }
    public List Notifications { get; set; }
    public Statistics Stats { get; set; }
    public List Activities { get; set; }
}

// 單一端點返回聚合數據
[HttpGet("dashboard")]
public async Task GetDashboardAsync()
{
    // 並行查詢多個獨立數據源
    var profileTask = _userRepo.GetProfileAsync(CurrentUserId);
    var notifTask = _notifRepo.GetRecentAsync(CurrentUserId, 10);
    var statsTask = _statsRepo.GetAsync(CurrentUserId);
    var activityTask = _activityRepo.GetRecentAsync(CurrentUserId, 20);
    
    await Task.WhenAll(profileTask, notifTask, statsTask, activityTask);
    
    return new DashboardData
    {
        Profile = profileTask.Result,
        Notifications = notifTask.Result,
        Stats = statsTask.Result,
        Activities = activityTask.Result
    };
}

// 多結果集單次查詢 (存儲過程/批次 SQL)
using (var reader = await cmd.ExecuteReaderAsync())
{
    var users = reader.ReadAll();      // 結果集 1
    await reader.NextResultAsync();
    var orders = reader.ReadAll();    // 結果集 2
    await reader.NextResultAsync();
    var stats = reader.ReadAll();      // 結果集 3
}

性能優化檢查清單

  1. [ ] 生產環境啟用 Gzip/Brotli 壓縮
  2. [ ] 核心接口遷移至 System.Text.Json/Jil
  3. [ ] 熱點數據接入 Redis 緩存
  4. [ ] 高頻寫入接口引入消息隊列異步處理
  5. [ ] 數據庫關鍵查詢優化索引、避免全表掃描
  6. [ ] 異步全鏈路貫通,無 `.Result`/`.Wait()` 阻塞
  7. [ ] 聚合接口減少前端請求數
  8. [ ] 監控 APM (Application Insights / SkyWalking / Zipkin) 定位瓶頸

性能優化是系統工程,而非單點技巧。建立度量基線、持續 Profiling、針對性優化,才能構建高吞吐、低延遲的 Web API 系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved