1. 介绍
随着互联网的不断发展,高并发、大流量的问题已经成为互联网企业发展中需要面对和解决的重要问题。在这种情况下,如何进行流量的限制和控制,保证系统的安全性和稳定性成为了企业所必须要面对和解决的问题。针对这种情况,Redis常见限流算法应运而生。
2. 限流算法原理
2.1 令牌桶算法
令牌桶算法是一种比较常见的限流算法,具体思路为系统中有一个令牌桶,以一定的速度不断生成令牌。当请求到来时,先判断桶中是否有令牌,如果有则允许这个请求通过,从而消耗一个令牌,如果桶中没有令牌,则拒绝该请求。同时,为了应对突发的请求,在桶未满的情况下,会在桶里备有一定数量的令牌,以应对一定程度的突发流量。
令牌桶算法的优点在于其对突发流量的处理能力,可以有效应对短时间内大量的请求。但是,对于长时间的请求,它就没有办法有效地应对了。
// 令牌桶算法Python实现
class TokenBucket:
"""
令牌桶
"""
def __init__(self, rate, capacity):
self._rate = rate
self._capacity = capacity
self._size = capacity
self._last_consume_time = time.time()
def consume(self, tokens):
"""
消费tokens个令牌
:param tokens: 消费的令牌数
:return: True/False
"""
if tokens <= self._size + (time.time() - self._last_consume_time) * self._rate:
self._size = min(self._capacity, self._size + (time.time() - self._last_consume_time) * self._rate - tokens)
self._last_consume_time = time.time()
return True
else:
return False
2.2 漏桶算法
相对于令牌桶算法,漏桶算法不同的是,漏桶在请求到来时不管桶中剩余的水量是否足够,都会以恒定的速度进行出水,统计请求过来的流量。当桶的容量满了以后,就会拒绝请求。
相对于令牌桶算法,漏桶算法的优点则在于其对于长时间的请求的处理能力更强,可以更好的平滑流量。
// 漏桶算法Python实现
class LeakyBucket:
"""
漏桶
"""
def __init__(self, rate, capacity):
self._rate = rate
self._capacity = capacity
self._size = 0
self._last_consume_time = time.time()
def consume(self, tokens):
"""
消费tokens个请求流量
:param tokens: 消费的流量数
:return: True/False
"""
self._size -= (time.time() - self._last_consume_time) * self._rate
if self._size < 0:
self._size = 0
self._last_consume_time = time.time()
if self._size + tokens <= self._capacity:
self._size += tokens
return True
else:
return False
3. Redis常见限流算法
3.1 漏桶算法实现
在Redis中,可以使用redis-cli
实现漏桶算法:
// 创建漏桶
redis> SET key 0 EX 3600 NX
// 令桶中的数值递增,最大值为设定值
redis> EVAL "local current = tonumber(redis.call('get', KEYS[1]) or '0')
local incrby = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
current = math.min(current + incrby, capacity)
redis.call('set', KEYS[1], current)
return current" 1 key 1 10
3.2 令牌桶算法实现
同样地,在Redis中,可以使用redis-cli
实现令牌桶算法:
// 创建令牌桶
redis> SET key 1
redis> SET key:timestamp 0
// 减少桶中的令牌数量
redis> EVAL "local permits = tonumber(redis.call('get', KEYS[1]) or '0')
local burst = tonumber(ARGV[1])
local period = tonumber(ARGV[2])
local now = tonumber(redis.call('time')[1])
local delay = math.max(0, period - (now - tonumber(redis.call('get', KEYS[2]) or now)))
local requests = tonumber(ARGV[3])
local generated_permits = math.min(requests, burst - permits)
local stored_permits = math.floor(delay / period * burst)
local new_permits = generated_permits + stored_permits
redis.call('setex', KEYS[1], period, new_permits + permits)
redis.call('set', KEYS[2], now)
return new_permits + stored_permits" 2 key:permits 1 key:timestamp 10 1 1
4. 总结
在高并发场景下,流量限制和防止过载是必须的。Redis提供了漏桶算法和令牌桶算法来限制流量。这两种算法可以根据需求进行选择,令牌桶算法能够处理突发流量,而漏桶算法对于长时间的请求有更好的平滑处理能力。