前言
你有没有想过:双11秒杀系统是怎么扛住每秒几十万请求的?为什么系统不会被打垮?
限流器是保障系统稳定性的核心组件,通过限制请求速率,防止系统过载。
今天我们从零实现两种经典限流算法:
· 令牌桶(Token Bucket)------ 允许突发流量
· 漏桶(Leaky Bucket)------ 平滑流量
一、限流器核心原理
- 令牌桶算法
```
┌─────────────────────────────────────────────────────────────┐
│ 令牌桶 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 令牌(容量:burst) │ │
│ │ ● ● ● ● ● ● ● ● ● ● │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 以固定速率(rate/s)放入令牌 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 请求处理 │
│ 每个请求消耗一个令牌 │
│ 无令牌 → 拒绝 / 有令牌 → 通过 │
└─────────────────────────────────────────────────────────────┘
```
- 漏桶算法
```
┌─────────────────────────────────────────────────────────────┐
│ 漏桶 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 请求积水 │ │
│ │ \~ \~ \~ \~ \~ \~ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 以固定速率(rate/s)漏出请求 │
└─────────────────────────────────────────────────────────────┘
```
- 对比
特性 令牌桶 漏桶
突发流量 ✅ 允许 ❌ 平滑
实现复杂度 低 低
适用场景 秒杀/抢购 流量整形
二、完整代码实现
- 令牌桶限流器
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
// 令牌桶限流器
typedef struct token_bucket {
char key128; // 限流key(IP/用户/接口)
int rate; // 每秒放入令牌数
int capacity; // 桶容量(burst)
int tokens; // 当前令牌数
time_t last_update; // 最后更新时间
pthread_mutex_t mutex;
struct token_bucket *next;
} token_bucket_t;
// 限流器管理器
typedef struct rate_limiter {
token_bucket_t *buckets;
int default_rate;
int default_capacity;
pthread_mutex_t mutex;
} rate_limiter_t;
// 创建限流器管理器
rate_limiter_t *limiter_create(int default_rate, int default_capacity) {
rate_limiter_t *limiter = malloc(sizeof(rate_limiter_t));
limiter->buckets = NULL;
limiter->default_rate = default_rate;
limiter->default_capacity = default_capacity;
pthread_mutex_init(&limiter->mutex, NULL);
printf("限流器 创建,默认速率: %d/s, 容量: %d\n",
default_rate, default_capacity);
return limiter;
}
// 获取或创建令牌桶
token_bucket_t *get_or_create_bucket(rate_limiter_t *limiter, const char *key) {
pthread_mutex_lock(&limiter->mutex);
token_bucket_t *bucket = limiter->buckets;
while (bucket) {
if (strcmp(bucket->key, key) == 0) {
pthread_mutex_unlock(&limiter->mutex);
return bucket;
}
bucket = bucket->next;
}
bucket = malloc(sizeof(token_bucket_t));
strcpy(bucket->key, key);
bucket->rate = limiter->default_rate;
bucket->capacity = limiter->default_capacity;
bucket->tokens = limiter->default_capacity;
bucket->last_update = time(NULL);
pthread_mutex_init(&bucket->mutex, NULL);
bucket->next = limiter->buckets;
limiter->buckets = bucket;
pthread_mutex_unlock(&limiter->mutex);
return bucket;
}
// 令牌桶限流检查(返回1允许,0拒绝)
int token_bucket_allow(token_bucket_t *bucket) {
pthread_mutex_lock(&bucket->mutex);
time_t now = time(NULL);
int elapsed = now - bucket->last_update;
// 补充令牌
if (elapsed > 0) {
int new_tokens = elapsed * bucket->rate;
bucket->tokens = (bucket->tokens + new_tokens > bucket->capacity) ?
bucket->capacity : bucket->tokens + new_tokens;
bucket->last_update = now;
}
int allow = (bucket->tokens > 0);
if (allow) {
bucket->tokens--;
}
pthread_mutex_unlock(&bucket->mutex);
return allow;
}
// 设置桶参数
void token_bucket_set_rate(token_bucket_t *bucket, int rate, int capacity) {
pthread_mutex_lock(&bucket->mutex);
bucket->rate = rate;
bucket->capacity = capacity;
if (bucket->tokens > capacity) {
bucket->tokens = capacity;
}
pthread_mutex_unlock(&bucket->mutex);
}
```
- 漏桶限流器
```c
// 漏桶限流器
typedef struct leaky_bucket {
char key128;
int rate; // 每秒漏出请求数
int capacity; // 桶容量
int water; // 当前水位(排队请求数)
time_t last_update;
pthread_mutex_t mutex;
struct leaky_bucket *next;
} leaky_bucket_t;
// 漏桶限流检查
int leaky_bucket_allow(leaky_bucket_t *bucket) {
pthread_mutex_lock(&bucket->mutex);
time_t now = time(NULL);
int elapsed = now - bucket->last_update;
// 漏出请求
if (elapsed > 0) {
int leaked = elapsed * bucket->rate;
bucket->water = (bucket->water - leaked > 0) ?
bucket->water - leaked : 0;
bucket->last_update = now;
}
int allow = (bucket->water < bucket->capacity);
if (allow) {
bucket->water++;
}
pthread_mutex_unlock(&bucket->mutex);
return allow;
}
```
- 分布式限流(Redis实现)
```c
// Redis连接(模拟)
typedef struct redis_conn {
char host32;
int port;
} redis_conn_t;
// Redis限流器
typedef struct redis_rate_limiter {
redis_conn_t *conns;
int node_count;
char key_prefix64;
int rate;
int capacity;
int window_ms;
} redis_rate_limiter_t;
// Redis令牌桶(使用Lua脚本保证原子性)
const char *LUA_SCRIPT =
"local key = KEYS1"
"local rate = tonumber(ARGV1)"
"local capacity = tonumber(ARGV2)"
"local now = tonumber(ARGV3)"
"local requested = tonumber(ARGV4)"
""
"local data = redis.call('hmget', key, 'tokens', 'last_update')"
"local tokens = tonumber(data1) or capacity"
"local last_update = tonumber(data2) or now"
""
"local elapsed = now - last_update"
"if elapsed > 0 then"
" tokens = math.min(capacity, tokens + elapsed * rate)"
"end"
""
"local allowed = false"
"if tokens >= requested then"
" tokens = tokens - requested"
" allowed = true"
"end"
""
"redis.call('hset', key, 'tokens', tokens, 'last_update', now)"
"return allowed";
// 分布式限流检查
int redis_rate_limiter_allow(redis_rate_limiter_t *limiter, const char *key) {
// 实际应执行Lua脚本
// 这里简化模拟
return 1;
}
```
- 滑动窗口限流器
```c
// 滑动窗口限流器
typedef struct sliding_window {
char key128;
int limit; // 窗口内最大请求数
int window_ms; // 窗口大小(毫秒)
time_t *timestamps;
int timestamp_count;
int max_timestamps;
pthread_mutex_t mutex;
struct sliding_window *next;
} sliding_window_t;
// 滑动窗口限流检查
int sliding_window_allow(sliding_window_t *sw) {
pthread_mutex_lock(&sw->mutex);
time_t now = time(NULL);
time_t window_start = now - sw->window_ms / 1000;
// 清理过期时间戳
int valid_count = 0;
for (int i = 0; i < sw->timestamp_count; i++) {
if (sw->timestampsi >= window_start) {
sw->timestampsvalid_count++ = sw->timestampsi;
}
}
sw->timestamp_count = valid_count;
int allow = (sw->timestamp_count < sw->limit);
if (allow) {
if (sw->timestamp_count >= sw->max_timestamps) {
sw->max_timestamps *= 2;
sw->timestamps = realloc(sw->timestamps,
sizeof(time_t) * sw->max_timestamps);
}
sw->timestampssw-\>timestamp_count++ = now;
}
pthread_mutex_unlock(&sw->mutex);
return allow;
}
```
- 限流器管理
```c
// 多级限流器
typedef struct multi_limiter {
token_bucket_t *global; // 全局限流
token_bucket_t *per_ip; // IP限流
token_bucket_t *per_user; // 用户限流
token_bucket_t *per_api; // 接口限流
} multi_limiter_t;
// 多级限流检查
int multi_limiter_allow(multi_limiter_t *ml, const char *ip,
const char *user, const char *api) {
// 1. 全局限流
if (!token_bucket_allow(ml->global)) return 0;
// 2. IP限流
if (ip && !token_bucket_allow(ml->per_ip)) return 0;
// 3. 用户限流
if (user && !token_bucket_allow(ml->per_user)) return 0;
// 4. 接口限流
if (api && !token_bucket_allow(ml->per_api)) return 0;
return 1;
}
```
- 测试代码
```c
void test_token_bucket() {
printf("=== 令牌桶限流测试 ===\n\n");
rate_limiter_t *limiter = limiter_create(5, 10);
// 获取桶
token_bucket_t *bucket = get_or_create_bucket(limiter, "192.168.1.100");
printf("速率: %d/s, 容量: %d\n", bucket->rate, bucket->capacity);
printf("发送20个请求:\n");
int allowed = 0, denied = 0;
for (int i = 0; i < 20; i++) {
if (token_bucket_allow(bucket)) {
allowed++;
printf(" %2d: ✅ 允许 (剩余令牌: %d)\n", i+1, bucket->tokens);
} else {
denied++;
printf(" %2d: ❌ 拒绝\n", i+1);
}
usleep(100000); // 100ms
}
printf("\n结果: 允许 %d, 拒绝 %d\n", allowed, denied);
free(limiter);
}
void test_leaky_bucket() {
printf("\n=== 漏桶限流测试 ===\n\n");
leaky_bucket_t bucket;
strcpy(bucket.key, "test");
bucket.rate = 5;
bucket.capacity = 10;
bucket.water = 0;
bucket.last_update = time(NULL);
pthread_mutex_init(&bucket.mutex, NULL);
printf("速率: %d/s, 容量: %d\n", bucket.rate, bucket.capacity);
printf("发送20个请求:\n");
int allowed = 0, denied = 0;
for (int i = 0; i < 20; i++) {
if (leaky_bucket_allow(&bucket)) {
allowed++;
printf(" %2d: ✅ 允许 (当前水位: %d)\n", i+1, bucket.water);
} else {
denied++;
printf(" %2d: ❌ 拒绝\n", i+1);
}
usleep(50000);
}
printf("\n结果: 允许 %d, 拒绝 %d\n", allowed, denied);
}
int main() {
test_token_bucket();
test_leaky_bucket();
return 0;
}
```
三、编译和运行
```bash
gcc -o rate_limiter rate_limiter.c -lpthread
./rate_limiter
```
四、限流算法对比
特性 令牌桶 漏桶 滑动窗口
突发处理 ✅ ❌ ❌
平滑流量 ❌ ✅ ✅
实现复杂度 低 低 中
内存占用 小 小 大
适用场景 秒杀 流量整形 精确限流
五、总结
通过这篇文章,你学会了:
· 令牌桶算法(允许突发)
· 漏桶算法(平滑流量)
· 分布式限流(Redis实现)
· 滑动窗口限流
· 多级限流
限流器是系统稳定性的保障。掌握它,你就理解了高并发系统的保护机制。
下一篇预告:《从零实现一个分布式熔断器:Hystrix的核心设计》
评论区分享一下你用过什么限流方案~