面试题答案
一键面试1. 存储限流规则
- 使用Hash结构存储限流规则:
- 在Redis中,使用Hash数据结构存储限流规则。例如,以
rate_limit_rules
为key,每个field代表不同的限流维度组合,value存储对应的限流阈值。例如:
HSET rate_limit_rules "time:0-6|user_level:1" 100 HSET rate_limit_rules "time:6-18|user_level:2" 200
- 这里
time:0 - 6|user_level:1
表示在0 - 6点,用户等级为1的限流阈值为100;time:6 - 18|user_level:2
表示在6 - 18点,用户等级为2的限流阈值为200。
- 在Redis中,使用Hash数据结构存储限流规则。例如,以
- 时间维度处理:
- 对于时间维度,可以将一天划分为多个时间段,每个时间段作为限流规则的一部分。可以使用
CRON
表达式或者简单的时间段标识(如0 - 6
,6 - 18
,18 - 24
)。 - 如果需要更复杂的时间规则,如按周、按月等,可以使用Redis的
Sorted Set
来存储不同时间粒度的限流规则,并通过时间戳等方式进行排序和检索。
- 对于时间维度,可以将一天划分为多个时间段,每个时间段作为限流规则的一部分。可以使用
- 用户等级维度:
- 将用户等级作为限流规则的一个维度,在Hash结构的field中体现。这样可以方便地根据用户等级来获取对应的限流阈值。
2. 更新限流规则
- 原子更新操作:
- 使用Redis的
HSET
命令进行原子更新。例如,如果要更新time:6 - 18|user_level:2
的限流阈值为300,可以执行:
HSET rate_limit_rules "time:6-18|user_level:2" 300
- 这种方式保证了在高并发场景下更新限流规则的一致性,因为
HSET
命令是原子操作。
- 使用Redis的
- 发布 - 订阅模式:
- 为了通知应用程序限流规则的更新,可以使用Redis的发布 - 订阅(Pub/Sub)模式。当限流规则更新时,发布一个消息到特定的频道,所有订阅该频道的应用实例都会收到通知,然后重新加载限流规则。例如:
PUBLISH rate_limit_updates "time:6-18|user_level:2 updated to 300"
- 应用程序通过
SUBSCRIBE rate_limit_updates
命令订阅该频道,当收到消息后进行相应处理。
3. 高并发场景下保证性能和一致性
- Lua脚本:
- 在高并发场景下,使用Lua脚本来保证限流逻辑的原子性和性能。例如,编写一个Lua脚本实现限流逻辑:
local key = KEYS[1] local limit = tonumber(ARGV[1]) local current = tonumber(redis.call('GET', key) or "0") if current + 1 > limit then return 0 else redis.call('INCR', key) return 1 end
- 应用程序通过
EVAL
命令调用该Lua脚本,将限流key和阈值作为参数传递进去。这样可以在Redis服务器端原子地执行限流逻辑,避免了多次网络交互带来的性能问题和一致性问题。
- 分布式限流:
- 如果是分布式系统,可以使用Redis Cluster来实现分布式限流。每个应用实例从Redis Cluster中获取和更新限流规则,利用Redis Cluster的分布式特性来提高性能和可用性。
- 在分布式场景下,仍然使用Lua脚本来保证限流操作的原子性,确保在多个实例并发访问时,限流逻辑的一致性。
- 缓存预热:
- 在应用启动时,将常用的限流规则预先加载到本地缓存(如Guava Cache)中。这样在高并发请求时,可以先从本地缓存中获取限流规则,减少对Redis的访问压力,提高性能。当限流规则更新时,同时更新本地缓存和Redis中的数据。