面试题答案
一键面试技术方案
- 令牌桶算法:为每个用户分配一个令牌桶,令牌以固定速率生成并放入桶中。用户每次访问API时,从桶中取出一个令牌。如果桶中没有令牌,则拒绝访问。这样可以平滑地限制用户的访问速率。
- 使用缓存:将用户的令牌桶状态存储在缓存中,如Redis。缓存具有高读写性能,能够快速处理大量并发请求,保证系统性能。同时,缓存可以设置过期时间,方便管理用户的访问周期。
数据结构
- 哈希表:在缓存中,使用哈希表来存储每个用户的令牌桶信息。哈希表的键为用户ID,值为包含令牌桶状态(如当前令牌数量、上次更新时间等)的对象。
- 时间戳:记录令牌桶上次更新的时间,结合令牌生成速率,计算当前应该有的令牌数量。例如,在Java中可以使用
System.currentTimeMillis()
获取当前时间戳。
示例代码(Python + Redis实现)
import redis
import time
class TokenBucket:
def __init__(self, capacity, rate, user_id):
self.capacity = capacity
self.rate = rate
self.user_id = user_id
self.redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
self.last_update = self.redis_client.get(f'{self.user_id}:last_update')
if not self.last_update:
self.redis_client.set(f'{self.user_id}:last_update', int(time.time()))
self.redis_client.set(f'{self.user_id}:tokens', self.capacity)
else:
self.last_update = int(self.last_update)
def get_tokens(self):
now = int(time.time())
# 计算新生成的令牌数
new_tokens = (now - self.last_update) * self.rate
current_tokens = int(self.redis_client.get(f'{self.user_id}:tokens'))
current_tokens = min(current_tokens + new_tokens, self.capacity)
self.redis_client.set(f'{self.user_id}:tokens', current_tokens)
self.redis_client.set(f'{self.user_id}:last_update', now)
return current_tokens
def consume_token(self):
tokens = self.get_tokens()
if tokens >= 1:
self.redis_client.decr(f'{self.user_id}:tokens')
return True
return False
在API处理逻辑中,可以这样使用:
# 假设capacity为100,rate为10(每秒生成10个令牌)
token_bucket = TokenBucket(100, 10, 'user123')
if token_bucket.consume_token():
# 处理API请求
print('API request processed')
else:
print('Rate limit exceeded')