从零实现一个分布式限流器:令牌桶与漏桶

前言

你有没有想过:双11秒杀系统是怎么扛住每秒几十万请求的?为什么系统不会被打垮?

限流器是保障系统稳定性的核心组件,通过限制请求速率,防止系统过载。

今天我们从零实现两种经典限流算法:

· 令牌桶(Token Bucket)------ 允许突发流量

· 漏桶(Leaky Bucket)------ 平滑流量


一、限流器核心原理

  1. 令牌桶算法

```

┌─────────────────────────────────────────────────────────────┐

│ 令牌桶 │

│ ┌─────────────────────────────────────────────────────┐ │

│ │ 令牌(容量:burst) │ │

│ │ │ │

│ └─────────────────────────────────────────────────────┘ │

│ │ │

│ ▼ │

│ 以固定速率(rate/s)放入令牌 │

└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐

│ 请求处理 │

│ 每个请求消耗一个令牌 │

│ 无令牌 → 拒绝 / 有令牌 → 通过 │

└─────────────────────────────────────────────────────────────┘

```

  1. 漏桶算法

```

┌─────────────────────────────────────────────────────────────┐

│ 漏桶 │

│ ┌─────────────────────────────────────────────────────┐ │

│ │ 请求积水 │ │

│ │ \~ \~ \~ \~ \~ \~ │ │

│ └─────────────────────────────────────────────────────┘ │

│ │ │

│ ▼ │

│ 以固定速率(rate/s)漏出请求 │

└─────────────────────────────────────────────────────────────┘

```

  1. 对比

特性 令牌桶 漏桶

突发流量 ✅ 允许 ❌ 平滑

实现复杂度 低 低

适用场景 秒杀/抢购 流量整形


二、完整代码实现

  1. 令牌桶限流器

```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);

}

```

  1. 漏桶限流器

```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;

}

```

  1. 分布式限流(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;

}

```

  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;

}

```

  1. 限流器管理

```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;

}

```

  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的核心设计》


评论区分享一下你用过什么限流方案~

相关推荐
不负岁月无痕2 小时前
STL --C++哈希表封装unordered_map/set
c++·visualstudio·哈希算法·散列表·unordered
闻道且行之11 小时前
TurboOCR:基于PP-OCRv6的极速Windows离线OCR工具,深度解析3.4GB依赖背后的技术架构
c++·人工智能·python·qt·机器学习·ocr
c2385613 小时前
Bug 猎手入门指南
c++·算法·bug
玖玥拾15 小时前
C++ 数据结构 八大基础排序算法专题
数据结构·c++·算法·排序算法
YYYing.15 小时前
【C++大型项目之高性能服务器框架 (七) 】Socket与ByteArray模块
服务器·c++·后端·框架·高性能·c/c++
从零开始的代码生活_16 小时前
C++ list 原理与实践:双向链表、迭代器与简化实现
开发语言·c++·后端·学习·算法·链表·list
charlie11451419116 小时前
现代C++工程实践——WeakPtr 第二关:核心骨架与控制块
开发语言·c++
2401_8697695916 小时前
内容9 string 1
c++
ComputerInBook18 小时前
C & C++ 中的关键字 volatile 之详解
c语言·c++·volatile·编译器优化