drf限流模块-源码翻译及解读【django】
drf-限流源码解读
发邮箱的频率,上传头像的频率
不知道是我的问题还是文档的问题,看不懂用法,尝试了很多最后失败告终,所以结果就是自己重新实现一个或者是看它的源码尝试理解
两百多行,直接全部发给gpt翻译并且加上中文注释
发现翻译成中文注释之后真的好像打开了一片新天地,原来密密麻麻的天数原来表达的意思是这么简单。
下面我们就一起来看看它的源码。
python
"""
提供各种限流策略。
"""
import time
from django.core.cache import cache as default_cache # 使用 Django 默认的缓存机制
from django.core.exceptions import ImproperlyConfigured # 导入配置异常
from rest_framework.settings import api_settings # 导入 DRF 的全局设置
class BaseThrottle:
"""
请求的限流基类。
"""
def allow_request(self, request, view):
"""
如果允许该请求,则返回 `True`,否则返回 `False`。
此方法必须被子类重写。
"""
raise NotImplementedError('.allow_request() 必须被重写')
def get_ident(self, request):
"""
通过解析 HTTP_X_FORWARDED_FOR 来识别请求来源的机器,
如果代理数量大于 0 则使用代理地址。
否则,使用 REMOTE_ADDR。
"""
xff = request.META.get('HTTP_X_FORWARDED_FOR') # 获取客户端真实 IP
remote_addr = request.META.get('REMOTE_ADDR') # 获取远程地址
num_proxies = api_settings.NUM_PROXIES # 获取代理数设置
if num_proxies is not None:
if num_proxies == 0 or xff is None:
return remote_addr
addrs = xff.split(',')
client_addr = addrs[-min(num_proxies, len(addrs))]
return client_addr.strip()
return ''.join(xff.split()) if xff else remote_addr # 如果有 XFF,则使用
def wait(self):
"""
可选地,返回推荐的等待时间(秒)。
可以被子类重写提供限流失败时的等待时间。
"""
return None
class SimpleRateThrottle(BaseThrottle):
"""
一个简单的基于缓存的限流实现,只需重写 `.get_cache_key()` 方法。
限流速率通过 `rate` 属性设置,格式为 '请求数/时间'。
时间单位可以是:('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')。
先前的请求信息存储在缓存中,用于限流判断。
"""
cache = default_cache # 使用 Django 缓存
timer = time.time # 使用系统时间戳
cache_format = 'throttle_%(scope)s_%(ident)s' # 缓存键的格式
scope = None # 限流范围,具体子类中定义
THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES # 从设置中获取限流速率
def __init__(self):
# 初始化时,解析限流速率
if not getattr(self, 'rate', None):
self.rate = self.get_rate()
self.num_requests, self.duration = self.parse_rate(self.rate)
def get_cache_key(self, request, view):
"""
返回用于限流的唯一缓存键。
必须被重写。
如果不进行限流,返回 `None`。
"""
raise NotImplementedError('.get_cache_key() 必须被重写')
def get_rate(self):
"""
获取允许的请求速率。
"""
if not getattr(self, 'scope', None):
msg = ("必须为 '%s' 限流器设置 `.scope` 或 `.rate`" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
return self.THROTTLE_RATES[self.scope]
except KeyError:
msg = "'%s' 范围没有设置默认的限流速率" % self.scope
raise ImproperlyConfigured(msg)
def parse_rate(self, rate):
"""
解析请求速率字符串,返回一个二元组:
<允许的请求数>, <时间周期(秒)>
"""
if rate is None:
return (None, None)
num, period = rate.split('/')
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration)
def allow_request(self, request, view):
"""
检查请求是否应该被限流。
成功时调用 `throttle_success`。
失败时调用 `throttle_failure`。
"""
if self.rate is None:
return True
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
self.history = self.cache.get(self.key, []) # 从缓存获取历史请求记录
self.now = self.timer() # 获取当前时间戳
# 移除已超过限流时长的请求记录
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure() # 超出限流,限流失败
return self.throttle_success() # 请求成功
def throttle_success(self):
"""
将当前请求的时间戳和键插入缓存。
"""
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True
def throttle_failure(self):
"""
请求由于限流而失败时调用。
"""
return False
def wait(self):
"""
返回建议的下一次请求时间(秒)。
"""
if self.history:
remaining_duration = self.duration - (self.now - self.history[-1])
else:
remaining_duration = self.duration
available_requests = self.num_requests - len(self.history) + 1
if available_requests <= 0:
return None
return remaining_duration / float(available_requests)
class AnonRateThrottle(SimpleRateThrottle):
"""
限制匿名用户的 API 调用速率。
请求的 IP 地址将用作唯一的缓存键。
"""
scope = 'anon' # 范围为 'anon' 表示匿名用户限流
def get_cache_key(self, request, view):
if request.user and request.user.is_authenticated:
return None # 只对未认证的请求进行限流
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request) # 使用 IP 作为唯一标识
}
class UserRateThrottle(SimpleRateThrottle):
"""
限制已登录用户的 API 调用速率。
对于已认证用户,使用用户 ID 作为缓存键。
对于匿名请求,使用请求的 IP 地址。
"""
scope = 'user' # 范围为 'user' 表示用户限流
def get_cache_key(self, request, view):
if request.user and request.user.is_authenticated:
ident = request.user.pk # 认证用户使用用户 ID 作为唯一标识
else:
ident = self.get_ident(request) # 未认证用户使用 IP
return self.cache_format % {
'scope': self.scope,
'ident': ident
}
class ScopedRateThrottle(SimpleRateThrottle):
"""
根据 API 的不同部分限制 API 调用速率。
任何具有 `throttle_scope` 属性的视图都会受到限流。
缓存键将由用户 ID 和视图的限流范围组成。
"""
scope_attr = 'throttle_scope' # 从视图中获取限流范围
def __init__(self):
# 重写 SimpleRateThrottle 的初始化,因为在被视图调用之前不能确定速率
pass
def allow_request(self, request, view):
# 只有在视图调用时我们才能确定 scope
self.scope = getattr(view, self.scope_attr, None)
# 如果视图没有 `throttle_scope` 属性,则总是允许请求
if not self.scope:
return True
# 通过 `get_rate` 获取允许的请求速率
self.rate = self.get_rate()
self.num_requests, self.duration = self.parse_rate(self.rate)
# 继续执行父类的 `allow_request` 逻辑
return super().allow_request(request, view)
def get_cache_key(self, request, view):
"""
如果视图没有 `throttle_scope`,则不应用限流。
否则通过将用户 ID 与视图的限流范围组合生成唯一的缓存键。
"""
if request.user and request.user.is_authenticated:
ident = request.user.pk
else:
ident = self.get_ident(request)
return self.cache_format % {
'scope': self.scope,
'ident': ident
}真实ip
xff = request.META.get('HTTP_X_FORWARDED_FOR') # 获取客户端真实 IP remote_addr = request.META.get('REMOTE_ADDR') # 获取远程地址 num_proxies = api_settings.NUM_PROXIES # 获取代理数设置 这三者的区别
这三者的区别在于它们获取的 IP 地址来源不同,具体来说:
HTTP_X_FORWARDED_FOR:HTTP_X_FORWARDED_FOR是一个 HTTP 头字段,用于记录客户端经过代理服务器或负载均衡时的真实 IP 地址。代理服务器通常会将客户端的 IP 地址添加到该字段中。如果有多个代理,IP 地址会以逗号分隔,按顺序记录每个代理传递的 IP 地址。- 用途:在有代理或负载均衡的情况下,用来获取客户端的原始 IP 地址,而不是代理服务器的 IP。
REMOTE_ADDR:REMOTE_ADDR是直接从 TCP 连接获取的远程 IP 地址,它表示与服务器建立连接的客户端的 IP,通常是代理服务器或负载均衡器的 IP 地址,而不是客户端的真实 IP。- 用途:用于没有代理或负载均衡情况下获取请求的来源 IP 地址。
NUM_PROXIES:NUM_PROXIES是一个配置项,用于指定服务器在处理X-Forwarded-For头字段时,应该忽略多少个代理。它决定服务器应该读取从右到左的第几个 IP 作为客户端的真实 IP。- 用途:用于确保即使请求经过多个代理或负载均衡,依然能准确地获取客户端的真实 IP 地址。
while self.history and self.history[-1] <= self.now - self.duration: self.history.pop()
这里不是有删除的操作吗,再设置有效期是不是重复了
确实,在限流代码中存在一个移除历史记录的操作:
python
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()这段代码的作用是:清除超过限流时间段的请求历史记录。它遍历 self.history,并删除那些时间戳早于当前时间减去限流时间段 self.duration 的记录。
而紧接着的缓存设置操作是:
python
self.cache.set(self.key, self.history, self.duration)这一步是将最新的 self.history 列表缓存起来,并且设置缓存的有效期 self.duration。
看起来这两者似乎有点重复,但实际上它们的目标是不同的:
- 移除历史记录(pop 操作):它是为了确保
self.history中只保留在限流时间段内的请求时间戳,以便准确地判断用户的请求是否超出限额。即这个操作是为了清理无效的历史请求。 - 设置缓存有效期:这一步的目的是将新的
self.history列表保存到缓存中,并且设置其有效期为self.duration秒。这样可以确保在缓存中存储的记录会在一段时间后自动失效。
因此,删除历史记录和设置缓存有效期其实并不冲突:
- 删除历史记录 是为了让
self.history中的请求信息始终保持在有效的限流时间范围内。 - 设置缓存有效期 是为了确保缓存中的数据在
self.duration时间后自动过期,防止无限制存储。
它们是限流机制中相辅相成的两部分,分别从不同的角度处理时间过期问题。