重庆市建设工程造价管理协会网站莱芜网络推广公司哪里有
重庆市建设工程造价管理协会网站,莱芜网络推广公司哪里有,详情页设计与制作,衡水市建设局网站#x1f6e1;️ 前言#xff1a;你的验证码挡不住“打码平台”
做过秒杀的都知道#xff0c;活动开始前 1 秒#xff0c;QPS 会瞬间飙升 100 倍。
你以为加上图形验证码就能挡住机器人#xff1f;
Too Simple。 现在的黑灰产早已接入了 OCR 识别和人工打码平台#xff0c…️ 前言你的验证码挡不住“打码平台”做过秒杀的都知道活动开始前 1 秒QPS 会瞬间飙升 100 倍。你以为加上图形验证码就能挡住机器人Too Simple。现在的黑灰产早已接入了 OCR 识别和人工打码平台验证码在他们面前形同虚设反而降低了真实用户的体验。真正有效的防刷手段是基于行为特征的限流。比如限制同一个 UserID 在 1 分钟内只能请求 5 次接口。很多同学会说“这简单RedisINCR计数不就行了”那你就掉坑里了。简单的计数器算法存在严重的**“临界突发流量”**问题。今天我们不仅要聊透算法还要用Redis Lua ZSet实现一套工业级的滑动窗口限流器让黑客的脚本彻底失效 痛点计数器算法的“死穴”假设我们限制1 分钟不超过 100 次。00:00:59时黑客发了 100 个请求没超限。00:01:01时计数器清零黑客又发了 100 个请求没超限。结果在 59秒 到 01秒 这短短2 秒内系统承受了200 个请求这就是固定窗口Fixed Window的缺陷。我们需要滑动窗口Sliding Window让窗口随着时间流动精准控制任意 60 秒内都不能超限。原理对比图滑动窗口_优势固定窗口_缺陷通过通过系统崩溃判定范围统计总量大于100小于100当前时间往前推 1 分钟请求进入计算窗口内请求数拒绝请求放行计数器 A00:59 发送 100 次计数器 B01:01 发送 100 次击穿限流2秒内通过 200 次️ 核心实现Redis ZSet Lua 的魔法在分布式系统中要实现滑动窗口Redis 的 ZSet (Sorted Set)是绝佳的数据结构。Keylimit:api:{userId}Value请求的唯一 IDUUIDScore当前时间戳毫秒算法逻辑每当一个请求进来移除删掉 ZSet 中时间戳在“窗口之外”的老数据 (ZREMRANGEBYSCORE)。统计计算 ZSet 中剩余的元素数量 (ZCARD)。判断如果数量 阈值则将当前请求加入 ZSet (ZADD) 并放行否则拒绝。续期设置 Key 的过期时间防止冷数据占用内存。为什么必须用 Lua上述 4 个步骤必须是原子性的如果在“统计”和“加入”之间并发了 100 个线程限流就会失效。Lua 脚本能保证这 4 步像执行一条命令一样完成。 代码实战手写 Lua 限流脚本将以下脚本保存为sliding_window.lua-- KEYS[1]: 限流 Key例如 limit:order:user_123-- ARGV[1]: 窗口时间毫秒例如 60000 (1分钟)-- ARGV[2]: 限流阈值例如 5-- ARGV[3]: 当前时间戳毫秒-- ARGV[4]: 请求唯一ID (防止 Member 重复)localkeyKEYS[1]localwindow_timetonumber(ARGV[1])locallimit_counttonumber(ARGV[2])localcurrent_timetonumber(ARGV[3])localmember_idARGV[4]-- 1. 移除窗口之前的数据核心滑动-- 也就是移除 score (当前时间 - 窗口时间) 的元素localmin_score0localmax_scorecurrent_time-window_time redis.call(ZREMRANGEBYSCORE,key,min_score,max_score)-- 2. 统计当前窗口内的请求数localcurrent_countredis.call(ZCARD,key)-- 3. 判断是否超限ifcurrent_countlimit_countthen-- 未超限加入当前请求redis.call(ZADD,key,current_time,member_id)-- 设置过期时间窗口时间 1秒缓冲避免僵尸 Keyredis.call(PEXPIRE,key,window_time1000)return1-- 允许通过elsereturn0-- 拒绝请求endJava 端调用工具类AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalDefaultRedisScriptLongLIMIT_SCRIPT;static{LIMIT_SCRIPTnewDefaultRedisScript();LIMIT_SCRIPT.setScriptText(...上面的Lua代码...);// 生产环境建议从文件读取LIMIT_SCRIPT.setResultType(Long.class);}publicbooleanisAllowed(StringuserId,Stringaction,intlimit,intwindowMs){Stringkeylimit:action:userId;longcurrentTimeSystem.currentTimeMillis();StringrequestIdUUID.randomUUID().toString();LongresultredisTemplate.execute(LIMIT_SCRIPT,Collections.singletonList(key),String.valueOf(windowMs),String.valueOf(limit),String.valueOf(currentTime),requestId);returnresult!nullresult1L;} 算法大比拼什么时候用哪个你可能会问“Guava RateLimiter 也是限流有啥区别”算法原理优点缺点适用场景计数器Redis INCR实现最简单有临界突发流量问题粗粒度限制如每天发码次数滑动窗口Redis ZSet精准控制无临界问题ZSet 耗内存不适合超大并发秒杀防刷精准 API 限流令牌桶Guava/Nginx支持预热允许突发此时此刻必须有令牌才能过网关层限流保护后端服务结论网关层全局保护用令牌桶Token Bucket。业务层防刷单用滑动窗口Sliding Window。因为防刷需要针对单个 UserID进行精准的时间窗口统计绝不能让黑客钻了“临界点”的空子。 总结秒杀系统的防刷本质上是一场成本的博弈。我们无法完全杜绝脚本但我们可以提高他们的攻击成本。通过Redis Lua Sliding Window我们迫使黑客必须拥有海量的真实账号和 IP 才能绕过限制这在经济上可能已经让攻击变得“不划算”了。这才是架构师的安全之道。博主留言你的系统中还在用简单的AtomicInteger做限流吗在评论区回复“Lua”我发给你一份《Redis 限流脚本合集含滑动窗口、令牌桶 Lua 版》复制粘贴立刻提升系统防御力