API 介面限流完整技術指南

目錄

  1. 前言
  2. 為什麼需要限流
  3. 常見限流策略
  4. .NET Core 實作方案
  5. 分散式限流方案
  6. 最佳實踐建議
  7. 環境配置建議
  8. 效能測試方法
  9. 結論

前言

在現代微服務架構中,API 限流(Rate Limiting)是一個不可或缺的重要機制。它能確保系統的穩定性和可用性,防止API被過度使用或遭受惡意攻擊。本文將詳細介紹 API 限流的概念、策略以及在 .NET Core 中的具體實作方法。

為什麼需要限流

實作 API 限流有以下幾個重要原因:

  1. 保護系統資源:防止單一客戶端消耗過多系統資源
  2. 確保服務品質:為所有使用者提供穩定的服務體驗
  3. 防止惡意攻擊:降低 DDoS 攻擊的影響
  4. 控制成本:特別是在使用雲端服務時,可以有效控制資源使用成本

常見限流策略

限流策略 實現複雜度 內存消耗 精確度 突發流量處理 分佈式實現
固定窗口
滑動窗口
令牌桶
漏桶

1. 固定窗口計數器(Fixed Window Counter)

最簡單的限流策略,在固定的時間窗口內計算請求次數。
基本原理:

  1. 設定一個固定的時間窗口(比如1分鐘)
  2. 在這個窗口內允許的最大請求數(比如100次)
  3. 每當有新請求時,檢查當前窗口內的請求數是否超過限制
    舉個例子:
    假設我們限制每分鐘最多100個請求:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 簡單示意
    current_minute = 13:01
    最大請求數 = 100
    當前計數 = 0

    當收到請求時:
    如果 current_minute 還沒過:
    當前計數 += 1
    如果 當前計數 <= 最大請求數:
    允許請求
    否則:
    拒絕請求
    否則:
    重設計數器為1
    更新current_minute
    允許請求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public class FixedWindowRateLimiter
    {
    private readonly int _limit;
    private readonly TimeSpan _window;
    private int _counter;
    private DateTime _lastReset;
    private readonly object _lock = new object(); // 加入鎖以支援併發

    public FixedWindowRateLimiter(int limit, TimeSpan window)
    {
    _limit = limit;
    _window = window;
    _lastReset = DateTime.UtcNow;
    }

    public bool ShouldAllowRequest()
    {
    lock (_lock) // 確保線程安全
    {
    var now = DateTime.UtcNow;
    if (now - _lastReset > _window)
    {
    _counter = 0;
    _lastReset = now;
    }

    if (_counter >= _limit)
    return false;

    _counter++;
    return true;
    }
    }
    }
    // 創建限流器:每分鐘最多100個請求
    var limiter = new FixedWindowRateLimiter(100, TimeSpan.FromMinutes(1));

    // 使用
    if (limiter.ShouldAllowRequest())
    {
    // 處理請求
    }
    else
    {
    // 拒絕請求
    }

優點:

  1. 實現簡單
  2. 內存佔用少
  3. 容易理解

缺點:

  1. 邊界問題:
    假設限制是每分鐘100次請求
    用戶可能在 13:00:59 發送100個請求
    然後在 13:01:01 又發送100個請求
    實際上在2秒內發送了200個請求,可能造成系統壓力

  2. 突發流量不平滑:
    窗口切換時會立即重設計數器,可能導致流量不均勻

適用場景:

  1. 簡單的 API 限流
  2. 單機應用的資源控制
  3. 對精確度要求不高的場景
  4. 適合做為快速原型或簡單實現

不適用場景:

  1. 分佈式系統
  2. 高併發環境
  3. 需要平滑限流的場景
  4. 對限流精確度要求高的場景

可能的優化方向:

  1. 使用 Redis 實現分佈式限流
  2. 新增監控和統計功能
  3. 實現重試機制
  4. 加入日誌記錄

程式碼實現注意事項:

  1. 使用 UTC 時間避免時區問題
  2. 考慮併發安全
  3. 注意時間計算的精確度
  4. 考慮記憶體使用效率

與其他限流演算法比較:

  1. 比滑動窗口簡單,但精確度低
  2. 比令牌桶少佔用記憶體
  3. 比漏桶實現簡單,但控制粒度較粗

實際應用建議:

  1. 在簡單場景下使用
  2. 配合監控系統
  3. 根據實際需求調整窗口大小
  4. 考慮是否需要持久化計數器

2. 滑動窗口計數器(Sliding Window Counter)

比固定窗口更精確的限流方式,能避免窗口邊界問題。
基本原理:

  1. 把時間窗口分成更小的時間片(bucket)
  2. 使用滾動方式統計,保持更平滑的限流效果

舉個具體例子:
假設要限制每分鐘最多100個請求,我們可以:

  • 把1分鐘分成6個10秒的小窗口
  • 持續記錄最近6個小窗口的請求數
  • 隨著時間推移,持續滾動更新這些小窗口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 可能的優化點:
// 1. 使用 ConcurrentDictionary 代替 Dictionary + lock
// 2. 使用快取來減少 Sum() 操作的頻率
// 3. 定期清理而不是每次請求時清理過期數據
public class SlidingWindowRateLimiter
{
private readonly Dictionary<long, int> _windows = new();
private readonly int _limit;
private readonly int _windowMilliseconds;
private readonly int _bucketSizeMillis;
private readonly object _lock = new();

public SlidingWindowRateLimiter(int limit, int windowSeconds, int bucketSizeMillis = 1000)
{
_limit = limit;
_windowMilliseconds = windowSeconds * 1000;
_bucketSizeMillis = bucketSizeMillis;
}

public bool ShouldAllowRequest()
{
lock (_lock)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var currentBucket = now - (now % _bucketSizeMillis);
var windowStart = now - _windowMilliseconds;

// 清除過期的窗口
_windows.Keys.Where(k => k < windowStart).ToList()
.ForEach(k => _windows.Remove(k));

// 計算當前窗口內的請求總數
var requestCount = _windows.Values.Sum();
if (requestCount >= _limit)
return false;

// 新增新請求到當前bucket
if (!_windows.ContainsKey(currentBucket))
_windows[currentBucket] = 0;
_windows[currentBucket]++;

return true;
}
}
}
// 創建一個限流器:每分鐘最多100個請求,bucket大小為1秒
var rateLimiter = new SlidingWindowRateLimiter(100, 60, 1000);

// 使用
if (rateLimiter.ShouldAllowRequest())
{
// 處理請求
}
else
{
// 拒絕請求
}
1
2
3
4
時間軸:  [10s] [10s] [10s] [10s] [10s] [10s]
初始: [12] [20] [15] [13] [10] [5] = 75個請求

滑動後: [20] [15] [13] [10] [5] [8] = 71個請求

優點:

  1. 更平滑的限流效果
  2. 避免了固定窗口的邊界問題
  3. 更精確的流量控制

使用場景:

  1. API限流:每分鐘限制用戶API調用次數
  2. 流量控制:限制每秒數據庫查詢次數
  3. 服務保護:限制單一IP的訪問頻率
  4. 資源訪問控制:限制檔案下載速率

注意事項:

  1. 需要更多的內存來存儲各個小窗口的計數
  2. 實現相對複雜
  3. 需要定期清理過期的計數數據

實際應用建議:

  1. 根據實際需求選擇合適的窗口大小和小窗口大小
  2. 考慮使用 Redis 等分佈式存儲來實現分佈式環境下的限流
  3. 可以配合其他限流演算法一起使用,達到更好的效果

實際應用中的數值參考:

  • 小窗口大小建議值:100ms ~ 1s
  • 總窗口大小建議值:
    • API限流:1s ~ 60s
    • 爬蟲限流:1min ~ 1hour
    • 業務限流:根據具體場景定義

比較:

  • 比固定窗口更平滑
  • 比令牌桶實現稍簡單
  • 比漏桶更靈活

3. 令牌桶算法(Token Bucket)

令牌桶算法是一種更靈活的限流策略,特別適合處理突發流量。
基本原理:

  1. 系統以固定速率往桶中放入令牌
  2. 桶可以儲存固定數量的令牌(桶容量)
  3. 每個請求需要消耗一個或多個令牌
  4. 如果桶中令牌不足,請求將被拒絕
  5. 即使桶是空的,令牌仍會持續生成並加入桶中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class TokenBucketRateLimiter
{
private readonly int _bucketCapacity;
private readonly int _tokensPerSecond;
private double _currentTokens;
private DateTime _lastRefillTime;
private readonly object _lock = new object();

public TokenBucketRateLimiter(int bucketCapacity, int tokensPerSecond)
{
_bucketCapacity = bucketCapacity;
_tokensPerSecond = tokensPerSecond;
_currentTokens = bucketCapacity;
_lastRefillTime = DateTime.UtcNow;
}

public bool ShouldAllowRequest(int tokens = 1)
{
lock (_lock)
{
RefillTokens();

if (_currentTokens >= tokens)
{
_currentTokens -= tokens;
return true;
}

return false;
}
}

private void RefillTokens()
{
var now = DateTime.UtcNow;
var timeElapsed = (now - _lastRefillTime).TotalSeconds;
var tokensToAdd = timeElapsed * _tokensPerSecond;

_currentTokens = Math.Min(_bucketCapacity, _currentTokens + tokensToAdd);
_lastRefillTime = now;
}

// 獲取當前令牌數量(用於監控)
public double GetCurrentTokens()
{
lock (_lock)
{
RefillTokens();
return _currentTokens;
}
}
}
// 創建限流器:桶容量100,每秒產生10個令牌
var rateLimiter = new TokenBucketRateLimiter(100, 10);

// 基本使用
if (rateLimiter.ShouldAllowRequest())
{
// 處理請求
}

// 消耗多個令牌的使用場景
if (rateLimiter.ShouldAllowRequest(5)) // 大請求消耗5個令牌
{
// 處理大請求
}

優點:

  1. 支援突發流量(最大允許突發等於桶容量)
  2. 令牌生成速率穩定,可以更好地保護系統
  3. 支援不同權重的請求(可消耗不同數量的令牌)
  4. 實現相對簡單,容易理解

缺點:

  1. 令牌桶容量和生成速率的設置需要經驗
  2. 分佈式環境實現相對複雜
  3. 可能存在輕微的時間精度問題

適用場景:

  1. API 限流
  2. 網絡流量控制
  3. 需要處理突發流量的場景
  4. 需要對不同請求進行差異化處理的場景

實現注意事項:

  1. 時間計算要使用 UTC 時間
  2. 需要考慮併發安全
  3. 浮點數計算可能存在精度問題
  4. 令牌生成速率要根據系統能力設置

分佈式實現建議:

  1. 使用 Redis 實現:
    • INCR:計數器
    • EXPIRE:設置過期時間
    • Lua 腳本:保證原子性
  2. 使用 Redis 時間戳記錄上次更新時間
  3. 分佈式鎖確保併發安全

效能優化建議:

  1. 使用批次令牌生成
  2. 緩存當前令牌數量
  3. 使用異步方式生成令牌
  4. 實現令牌預熱機制

監控指標:

  1. 令牌生成速率
  2. 當前桶內令牌數量
  3. 請求被拒絕率
  4. 令牌使用率

與其他限流演算法比較:

  1. 比固定窗口更平滑
  2. 比滑動窗口更靈活
  3. 比漏桶允許更多突發流量

4. 漏桶算法(Leaky Bucket)

漏桶算法提供了最嚴格的速率控制,適合需要穩定輸出的場景。
基本原理:

  1. 請求先進入桶中,桶有固定容量
  2. 桶以固定速率漏出請求
  3. 如果桶滿了,新請求會被拒絕
  4. 不論輸入速率如何,輸出速率都是恆定的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class LeakyBucketRateLimiter
{
private readonly Queue<DateTime> _bucket = new();
private readonly int _bucketCapacity;
private readonly TimeSpan _leakInterval;
private readonly object _lock = new object();
private DateTime _lastLeakTime;

public LeakyBucketRateLimiter(int bucketCapacity, TimeSpan leakInterval)
{
_bucketCapacity = bucketCapacity;
_leakInterval = leakInterval;
_lastLeakTime = DateTime.UtcNow;
}

public bool ShouldAllowRequest()
{
lock (_lock)
{
var now = DateTime.UtcNow;
LeakRequests(now);

if (_bucket.Count < _bucketCapacity)
{
_bucket.Enqueue(now);
return true;
}

return false;
}
}

private void LeakRequests(DateTime now)
{
// 計算從上次漏水到現在應該漏出多少請求
var elapsedTime = now - _lastLeakTime;
var leaksCount = (int)(elapsedTime.TotalMilliseconds / _leakInterval.TotalMilliseconds);

if (leaksCount > 0)
{
// 最多漏出當前桶中的所有請求
leaksCount = Math.Min(leaksCount, _bucket.Count);

for (int i = 0; i < leaksCount; i++)
{
_bucket.Dequeue();
}

_lastLeakTime = now;
}
}

// 獲取當前桶中請求數(用於監控)
public int GetCurrentQueueSize()
{
lock (_lock)
{
return _bucket.Count;
}
}
}
// 創建限流器:桶容量100,每100ms處理一個請求
var rateLimiter = new LeakyBucketRateLimiter(100, TimeSpan.FromMilliseconds(100));

// 使用
if (rateLimiter.ShouldAllowRequest())
{
// 處理請求
}
else
{
// 請求被拒絕
}

優點:

  1. 固定的流出速率,很好的流量整形效果
  2. 可以平滑地處理突發流量
  3. 具備削峰填谷的能力
  4. 保護下游系統的穩定性

缺點:

  1. 不支援突發流量
  2. 實現相對複雜
  3. 需要額外的隊列空間
  4. 可能造成請求的額外延遲

適用場景:

  1. 需要固定處理速率的場景
  2. 需要保護下游系統的場景
  3. 視頻流處理
  4. 消息佇列處理
  5. 數據庫寫入限流

實現注意事項:

  1. 時間計算精度
  2. 併發安全處理
  3. 記憶體使用效率
  4. 過期請求清理策略

分佈式實現建議:

  1. Redis 實現方案:
    • List 作為請求隊列
    • Sorted Set 存儲時間戳
    • Lua 腳本保證原子性
  2. 使用消息隊列系統
  3. 分佈式鎖確保一致性

效能優化建議:

  1. 批次處理漏出的請求
  2. 使用更高效的數據結構
  3. 實現請求優先級
  4. 動態調整漏出速率

監控指標:

  1. 當前桶內請求數
  2. 請求被拒絕率
  3. 平均處理延遲
  4. 漏出速率穩定性

與其他限流演算法比較:

  1. 比固定窗口更穩定
  2. 比滑動窗口實現複雜
  3. 比令牌桶更嚴格

.NET Core 實作方案

使用 AspNetCoreRateLimit 套件

最簡單的方式是使用現成的 AspNetCoreRateLimit 套件:

  1. 首先安裝套件:

    1
    dotnet add package AspNetCoreRateLimit
  2. Program.cs 中設定服務:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var builder = WebApplication.CreateBuilder(args);

// 加入記憶體快取
builder.Services.AddMemoryCache();

// 設定限流選項
builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));
builder.Services.Configure<IpRateLimitPolicies>(builder.Configuration.GetSection("IpRateLimitPolicies"));

// 加入相依服務
builder.Services.AddInMemoryRateLimiting();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

var app = builder.Build();

// 啟用 IP 限流中介軟體
app.UseIpRateLimiting();
  1. appsettings.json 中設定限流規則:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "192.168.0.0/24" ],
"ClientWhitelist": [ "dev-id", "trusted-app" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "1m",
"Limit": 100
}
]
}
}

自定義中介軟體實作

如果需要更靈活的控制,可以實作自己的限流中介軟體:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class RateLimitMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private readonly string _cacheKey = "RateLimit";

public RateLimitMiddleware(RequestDelegate next, IMemoryCache cache)
{
_next = next;
_cache = cache;
}

public async Task InvokeAsync(HttpContext context)
{
var ipAddress = context.Connection.RemoteIpAddress?.ToString();
var cacheKey = $"{_cacheKey}_{ipAddress}";

var rateLimiter = _cache.GetOrCreate(cacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
return new FixedWindowRateLimiter(100, TimeSpan.FromMinutes(1));
});

if (!rateLimiter.ShouldAllowRequest())
{
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.Response.Headers.Add("Retry-After", "60");
context.Response.Headers.Add("X-RateLimit-Limit", "100");
context.Response.Headers.Add("X-RateLimit-Remaining", "0");
context.Response.Headers.Add("X-RateLimit-Reset",
DateTimeOffset.UtcNow.AddMinutes(1).ToUnixTimeSeconds().ToString());

await context.Response.WriteAsync("Too many requests. Please try again later.");
return;
}

await _next(context);
}
}

分散式限流方案

在分散式系統中,建議使用 Redis 來實作限流機制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class RedisRateLimiter
{
private readonly IConnectionMultiplexer _redis;
private readonly string _keyPrefix;

public RedisRateLimiter(IConnectionMultiplexer redis, string keyPrefix)
{
_redis = redis;
_keyPrefix = keyPrefix;
}

public async Task<bool> ShouldAllowRequestAsync(string clientId, int limit, TimeSpan window)
{
var db = _redis.GetDatabase();
var key = $"{_keyPrefix}:{clientId}";

// 使用 Redis 的 MULTI 指令確保原子性
var tran = db.CreateTransaction();

// 獲取當前計數
var countTask = db.StringGetAsync(key);
var count = int.Parse((await countTask).ToString() ?? "0");

if (count >= limit)
return false;

// 增加計數並設置過期時間
await db.StringIncrementAsync(key);
await db.KeyExpireAsync(key, window);

return true;
}
}

使用範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DistributedRateLimitMiddleware
{
private readonly RequestDelegate _next;
private readonly RedisRateLimiter _rateLimiter;

public DistributedRateLimitMiddleware(RequestDelegate next,
IConnectionMultiplexer redis)
{
_next = next;
_rateLimiter = new RedisRateLimiter(redis, "ratelimit");
}

public async Task InvokeAsync(HttpContext context)
{
var clientId = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";

if (!await _rateLimiter.ShouldAllowRequestAsync(clientId, 100,
TimeSpan.FromMinutes(1)))
{
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.Response.WriteAsync("Rate limit exceeded");
return;
}

await _next(context);
}
}

最佳實踐建議

  1. 適當的限流閾值

    • 根據系統資源和業務需求設定合理的限流閾值
    • 可以為不同的 API 端點設定不同的限制
    • 考慮系統的實際承載能力進行設定
  2. 分層限流

    • IP 位址限流
    • 使用者身份限流
    • API 端點限流
    • 全域限流
  3. 監控和警報

    • 設置監控系統追蹤限流情況
    • 當限流次數異常時發出警報
    • 記錄限流事件以便分析
  4. 優雅的處理

    • 返回適當的 HTTP 429 狀態碼
    • 在響應標頭中提供重試時間建議
    • 提供清晰的錯誤訊息
    • 實作退避策略
  5. 快取考量

    • 使用分散式快取來支援水平擴展
    • 定期清理過期的限流記錄
    • 設置適當的快取過期時間

環境配置建議

開發環境

1
2
3
4
5
6
7
8
{
"RateLimiting": {
"Enabled": false,
"WhitelistEnabled": true,
"DefaultLimit": 1000,
"Period": "1m"
}
}

測試環境

1
2
3
4
5
6
7
8
9
{
"RateLimiting": {
"Enabled": true,
"WhitelistEnabled": true,
"DefaultLimit": 100,
"Period": "1m",
"MonitoringEnabled": true
}
}

生產環境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"RateLimiting": {
"Enabled": true,
"WhitelistEnabled": true,
"DefaultLimit": 60,
"Period": "1m",
"MonitoringEnabled": true,
"AlertingEnabled": true,
"Rules": [
{
"Endpoint": "/api/public/*",
"Limit": 30,
"Period": "1m"
},
{
"Endpoint": "/api/authenticated/*",
"Limit": 100,
"Period": "1m"
}
],
"IpWhitelist": [],
"ClientWhitelist": ["internal-service", "monitoring-service"],
"AlertThresholds": {
"RejectionRate": 0.1,
"RequestCount": 1000
}
}
}

生產環境配置重點:

  1. 較嚴格的限流規則
  2. 針對不同 API 路徑設定不同限制
  3. 啟用監控和警報機制
  4. 最小化白名單範圍
  5. 設定警報閾值

效能測試方法

使用 Apache JMeter 進行測試

  1. 基本測試計劃

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <jmeterTestPlan version="1.2" properties="5.0">
    <ThreadGroup guiclass="ThreadGroupGui" testname="API Rate Limit Test">
    <elementProp name="ThreadGroup.main_controller">
    <stringProp name="LoopController.loops">100</stringProp>
    <stringProp name="ThreadGroup.num_threads">50</stringProp>
    <stringProp name="ThreadGroup.ramp_time">10</stringProp>
    </elementProp>
    </ThreadGroup>
    </jmeterTestPlan>
  2. 測試場景

    • 正常負載測試:模擬正常使用情況
    • 突發負載測試:短時間內大量請求
    • 持續高負載測試:長時間維持高請求量
    • 限流觸發測試:確認限流機制生效
  3. 監控指標

    • 響應時間
    • 請求成功率
    • 限流觸發率
    • 系統資源使用情況

使用 K6 進行效能測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
stages: [
{ duration: '30s', target: 20 }, // 正常負載
{ duration: '1m', target: 100 }, // 逐漸增加
{ duration: '30s', target: 100 }, // 維持高負載
{ duration: '30s', target: 0 }, // 緩慢降低
],
};

export default function () {
const res = http.get('http://api.example.com/test');

check(res, {
'is status 200': (r) => r.status === 200,
'is rate limited': (r) => r.status !== 429,
});

sleep(1);
}

效能測試重點

  1. 基準測試

    • 在無限流情況下測試系統基準性能
    • 記錄關鍵指標作為參考值
  2. 限流閾值驗證

    • 驗證限流規則是否如預期運作
    • 測試不同限流策略的效果
  3. 系統穩定性測試

    • 長時間運行測試
    • 監控系統資源使用情況
    • 評估記憶體洩漏風險
  4. 擴展性測試

    • 測試分散式限流的效果
    • 評估系統橫向擴展能力

結論

API 限流是確保系統穩定性和可用性的關鍵機制。通過本文介紹的各種限流策略和實作方式,開發者可以根據實際需求選擇合適的解決方案。重點建議:

  1. 選擇適當的限流策略

    • 固定窗口:簡單直接,適合基本場景
    • 滑動窗口:更精確的控制,避免邊界問題
    • 令牌桶:適合處理突發流量
    • 漏桶:適合需要穩定出口速率的場景
  2. 分散式架構考量

    • 使用 Redis 等分散式解決方案
    • 確保限流規則在叢集中同步
    • 考慮資料一致性問題
  3. 監控和維護

    • 即時監控限流情況
    • 定期評估和調整限流規則
    • 建立完善的警報機制
  4. 持續優化

    • 根據監控數據調整限流策略
    • 定期進行效能測試
    • 及時應對新的挑戰

最後,記住限流不僅是一個技術問題,也是一個業務問題。在實作限流時,需要根據具體的業務需求和系統資源來制定合適的限流策略。通過合理的限流機制,我們可以在保護系統的同時,為使用者提供穩定且可靠的服務。

參考資料