资讯动态

Spring Cloud Gateway限流实战:Redis令牌桶与Lua原子性实现

发布时间:2026/10/22 9:31:15 来源:尧图企业网站定制
1. 网关限流的核心逻辑与方案选型1.1 为什么限流要放在网关层做过微服务架构的朋友应该都有体会限流这件事如果放到每个业务服务里各做各的最后一定会变成一场灾难。我见过一个项目订单服务用Guava的RateLimiter做单机限流用户服务用Sentinel做集群限流支付服务干脆在Nginx层加了个连接数限制结果大促的时候三个服务的限流阈值互相打架运维排查问题查了整整一个通宵。把限流统一收到网关层本质上是把流量治理这件事从业务逻辑里剥离出来。网关是所有外部请求进入系统的唯一入口在这个位置做限流有几个天然优势第一规则统一不会出现各服务标准不一致的情况第二业务代码零侵入开发同学不需要在业务逻辑里关心限流这件事第三可以基于全局视角做决策比如按API路径、按调用方、按请求参数等维度灵活配置。Gateway网关作为Spring Cloud生态里的第二代网关组件底层基于WebFlux和Reactor实现天生就是异步非阻塞的模型。这个特性对限流来说非常关键——限流操作本身会涉及令牌桶计算、Redis交互等IO操作如果用传统的Servlet阻塞模型高并发下限流器本身就可能成为瓶颈。Gateway的响应式模型让限流逻辑可以在不阻塞线程的前提下完成这是它相比Zuul 1.x的核心优势。1.2 限流算法的选择与对比限流算法这块市面上主流的就是那么几种但每种适用的场景差别很大选错了要么限不住要么误伤正常流量。计数器算法是最简单的维护一个时间窗口内的请求计数超过阈值就拒绝。实现容易但有个致命问题叫临界突变——假设限制每分钟100次如果前59秒来了99个请求第60秒又来了100个请求那这一秒内实际通过了199个请求系统可能直接被压垮。滑动窗口算法是对计数器的改进把时间窗口切成多个小格子随着时间推移滑动统计。能缓解临界问题但格子切得越细内存消耗越大精度和资源之间需要权衡。漏桶算法的核心是请求以固定速率流出不管进来多少请求处理速率恒定。这个特性适合做流量整形比如对接第三方接口时对方要求每秒只能调50次用漏桶就很合适。但漏桶不允许突发流量即使系统当前很空闲也只能按固定速率处理。令牌桶算法是我个人最推荐的方案也是Gateway官方RequestRateLimiter过滤器默认支持的算法。它的原理是系统以恒定速率往桶里放令牌每个请求需要拿走一个令牌才能通过。桶有容量上限当桶满时多余的令牌会丢弃。这个设计巧妙地兼顾了平均速率限制和突发流量允许——只要桶里有存货突发流量就能被消化掉。算法优点缺点适用场景计数器实现简单、内存占用小存在临界突变问题对精度要求不高的粗粒度限流滑动窗口精度较高、平滑内存消耗随精度增加需要较精确控制的API限流漏桶流出速率绝对恒定不允许突发、可能积压对接外部接口的流量整形令牌桶兼顾平均速率与突发实现相对复杂大多数微服务限流场景1.3 Gateway限流的两种落地路径在Gateway里做限流实际上有两条路可以走。第一条是内置的RequestRateLimiter过滤器这是Spring Cloud Gateway官方提供的限流组件底层依赖Redis和Lua脚本实现分布式限流。它的好处是开箱即用配置几个参数就能跑起来而且天然支持集群限流——多个Gateway实例共享同一个Redis令牌桶的状态是全局一致的。缺点是灵活性有限只能基于KeyResolver做限流维度的划分复杂的限流规则比如按权重、按优先级不太好实现。第二条是自定义GlobalFilter自己写限流逻辑。这条路灵活度最高你可以接入Sentinel、Resilience4j等成熟的限流框架也可以完全手写基于Redis的令牌桶。适合有特殊限流需求的场景比如需要动态调整阈值、需要多级限流组合、需要和业务指标联动等。代价就是开发量和维护成本更高。大多数项目我建议先用内置方案跑起来等遇到瓶颈或者有特殊需求了再考虑自定义。过早优化是万恶之源这句话在限流这件事上同样适用。2. 基于Redis的令牌桶限流实现细节2.1 RedisLua为什么是分布式限流的标配单机限流用Guava的RateLimiter就够了但网关通常是多实例部署的每个实例独立限流会导致实际通过流量是阈值的N倍N为实例数。比如你配置了每秒100次部署了4个Gateway实例那实际可能放进来400次请求这显然不是你想要的结果。分布式限流需要一个共享的存储来维护全局计数状态Redis是最常见的选择。但直接用Redis的INCR命令做计数有个问题获取当前值、判断是否超限、更新值这三步操作不是原子的。在高并发下多个请求可能同时读到相同的计数值然后都判断为未超限结果就是超限了。Lua脚本的价值就在这里。Redis执行Lua脚本时是单线程原子性的脚本内的所有命令要么全部执行要么全部不执行中间不会被其他请求打断。把读取令牌数→计算补充令牌→判断是否足够→扣减令牌→返回结果这一整套逻辑写进Lua脚本就能保证限流判断的原子性。Gateway内置的RedisRateLimiter就是基于这个思路实现的它使用了一个经过优化的Lua脚本核心逻辑大致是这样的-- 简化版令牌桶Lua脚本逻辑 local tokens_key KEYS[1] local timestamp_key KEYS[2] local rate tonumber(ARGV[1]) -- 令牌生成速率 local capacity tonumber(ARGV[2]) -- 桶容量 local now tonumber(ARGV[3]) -- 当前时间戳 local requested tonumber(ARGV[4]) -- 请求的令牌数 -- 获取上次填充时间和当前令牌数 local last_tokens tonumber(redis.call(get, tokens_key)) or capacity local last_refreshed tonumber(redis.call(get, timestamp_key)) or now -- 计算需要补充的令牌 local delta math.max(0, now - last_refreshed) local filled_tokens math.min(capacity, last_tokens (delta * rate)) -- 判断令牌是否足够 local allowed filled_tokens requested local new_tokens filled_tokens if allowed then new_tokens filled_tokens - requested end -- 更新Redis中的状态 redis.call(setex, tokens_key, 3600, new_tokens) redis.call(setex, timestamp_key, 3600, now) return { allowed and 1 or 0, new_tokens }这个脚本的精妙之处在于它不需要定时任务去补充令牌而是每次请求时根据时间差动态计算应该补充多少令牌。这种惰性填充的方式既节省了资源又保证了准确性。2.2 KeyResolver的设计与坑点KeyResolver决定了限流的维度也就是按什么来区分不同的限流桶。Gateway内置了几个常用的KeyResolver但实际项目里往往需要自定义。默认的PrincipalNameKeyResolver基于认证用户来限流PathKeyResolver基于请求路径HostAddrKeyResolver基于客户端IP。这几个够用但不完全够用比如你想按用户接口的组合维度限流或者想从请求头里取某个业务字段作为限流key就得自己写。Configuration public class RateLimiterConfig { // 按用户ID限流从请求头中获取 Bean public KeyResolver userKeyResolver() { return exchange - { String userId exchange.getRequest() .getHeaders().getFirst(X-User-Id); // 关键userId为空时必须给一个兜底值 return Mono.just(userId ! null ? userId : anonymous); }; } // 按IP路径组合限流 Bean public KeyResolver ipPathKeyResolver() { return exchange - { String ip exchange.getRequest().getRemoteAddress() .getAddress().getHostAddress(); String path exchange.getRequest().getPath().value(); return Mono.just(ip : path); }; } }这里有个我踩过的坑必须提醒KeyResolver返回的key绝对不能为空。如果返回Mono.empty()Gateway会直接抛异常请求返回500。我见过有同学从请求头取用户ID没登录的请求取不到就返回了空结果所有匿名请求全部报错。正确的做法是给一个兜底值比如anonymous或者直接用IP代替。另一个坑是key的粒度问题。如果你用PathKeyResolver那所有用户访问同一个接口共享一个令牌桶一个用户刷接口会把其他用户全挡住。如果你用HostAddrKeyResolver那同一个公司出口IP下的所有用户共享一个桶也容易误伤。实际项目里最常见的组合是用户维度接口维度既能防止单用户刷接口又不会因为某个接口被刷影响其他接口。2.3 限流参数的量化计算配置限流参数不能拍脑袋得根据实际业务量来算。我一般用这个公式来估算令牌生成速率replenishRate 接口的QPS峰值 × 安全系数安全系数通常取1.2到1.5给突发流量留点余量。比如一个查询接口日常QPS在200左右大促可能到500那replenishRate可以设成600。桶容量burstCapacity replenishRate × 允许突发的秒数这个参数决定了能承受多大的瞬时突发。如果设成replenishRate的2倍意味着允许在瞬间消耗掉2秒的令牌存量。设太大起不到限流效果设太小又容易误伤正常突发。请求令牌数requestedTokens 1大多数情况下一个请求消耗一个令牌就够了。但如果某些接口特别重比如导出报表、批量查询可以给这些接口配置消耗多个令牌实现按权重限流。参数含义推荐值注意事项replenishRate每秒生成令牌数QPS峰值×1.2~1.5太小误伤太大限不住burstCapacity桶最大容量replenishRate×2决定突发承受能力requestedTokens单请求消耗令牌默认1重接口可调大需与replenishRate匹配举个例子某商品详情接口日常QPS 300大促峰值800那replenishRate设1000burstCapacity设2000这样既能扛住大促峰值又能在流量异常时及时限流。3. 完整实操从零配置Gateway限流3.1 环境准备与依赖引入先把基础环境搭起来。假设你已经有一个Spring Cloud Gateway项目需要引入的依赖主要是两个dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis-reactive/artifactId /dependency注意第二个依赖必须是>spring: redis: host: 127.0.0.1 port: 6379 password: your_password database: 0 timeout: 3000ms lettuce: pool: max-active: 50 max-idle: 20 min-idle: 5 max-wait: 1000ms连接池参数要根据网关的并发量调整。max-active太小会导致获取连接超时太大会给Redis造成压力。一般按网关实例数×每实例并发线程数来估算留20%余量。3.2 路由级别的限流配置最简单的配置方式是在路由上直接挂RequestRateLimiter过滤器spring: cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 500 redis-rate-limiter.burstCapacity: 1000 redis-rate-limiter.requestedTokens: 1 key-resolver: #{userKeyResolver}这段配置的意思是访问/api/order/**的请求按用户维度限流每秒生成500个令牌桶容量1000每个请求消耗1个令牌。key-resolver的写法#{userKeyResolver}是SpEL表达式引用的是Spring容器里名为userKeyResolver的Bean。这里有个细节Bean的名字必须和表达式里写的一致大小写敏感。我见过有同学Bean名字叫userKeyResolver配置里写成#{UserKeyResolver}启动直接报错找不到Bean。如果不同接口需要不同的限流策略可以配置多个路由每个路由挂不同的KeyResolver和参数。比如查询接口宽松一点写入接口严格一点routes: - id: query-service uri: lb://query-service predicates: - Path/api/query/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1000 redis-rate-limiter.burstCapacity: 2000 key-resolver: #{ipKeyResolver} - id: write-service uri: lb://write-service predicates: - Path/api/write/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200 key-resolver: #{userKeyResolver}3.3 自定义限流响应默认情况下被限流的请求会返回429状态码响应体是空的。这个体验不太好前端拿到429不知道发生了什么。我们可以自定义限流后的响应Component public class CustomRateLimitHandler { Bean Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter rateLimitResponseFilter() { return (exchange, chain) - chain.filter(exchange) .then(Mono.fromRunnable(() - { if (exchange.getResponse().getStatusCode() HttpStatus.TOO_MANY_REQUESTS) { ServerHttpResponse response exchange.getResponse(); response.getHeaders().setContentType( MediaType.APPLICATION_JSON); String body {\code\:429,\message\:\请求过于频繁请稍后重试\}; DataBuffer buffer response.bufferFactory() .wrap(body.getBytes(StandardCharsets.UTF_8)); response.writeWith(Mono.just(buffer)).subscribe(); } })); } }这段代码通过一个GlobalFilter拦截响应当状态码是429时替换响应体。注意then方法是在请求处理完成后执行的这时候才能拿到最终的状态码。提示自定义响应体时要注意字符编码中文必须用UTF-8否则前端会看到乱码。另外响应头里的Content-Type也要设置正确不然浏览器可能不按JSON解析。3.4 动态调整限流阈值硬编码在配置文件里的阈值有个问题大促来了想临时调大得改配置重启网关这在生产环境是不可接受的。更好的做法是把阈值放到配置中心比如Nacos、Apollo支持动态刷新。Component RefreshScope public class DynamicRateLimiterConfig { Value(${rate.limit.order.replenishRate:500}) private int orderReplenishRate; Value(${rate.limit.order.burstCapacity:1000}) private int orderBurstCapacity; Bean public KeyResolver dynamicKeyResolver() { return exchange - { String userId exchange.getRequest() .getHeaders().getFirst(X-User-Id); return Mono.just(userId ! null ? userId : anonymous); }; } // 通过配置中心动态调整阈值 public void updateRateLimit(String routeId, int rate, int capacity) { // 更新配置中心的值触发RefreshScope刷新 } }配合配置中心的监听机制运维同学可以在大促前把阈值调高大促后再调回来全程不需要重启服务。4. 限流实战中的疑难杂症与排查4.1 限流不生效的常见原因限流配置了但不起作用这是最常见的问题。我整理了一个排查清单按优先级从上往下查排查项可能原因解决方法过滤器顺序限流过滤器被其他过滤器覆盖检查filter order确保限流在最前KeyResolverBean未注册或名字不匹配检查SpEL表达式和Bean名称Redis连接连接失败导致限流降级查看Redis连接日志和网络路由匹配请求未命中配置的路由开启Gateway调试日志确认参数单位误把分钟当秒配置确认replenishRate单位是秒其中过滤器顺序这个问题最隐蔽。Gateway的过滤器有优先级如果某个GlobalFilter的order值比RequestRateLimiter小它就会先执行可能修改了请求路径或请求头导致KeyResolver取不到正确的值。我一般把限流过滤器的order设成-1确保它在最前面执行。Redis连接失败导致限流降级也是个坑。默认情况下如果Redis不可用RedisRateLimiter会抛异常请求直接失败。但有些项目配置了降级策略Redis挂了就放行所有请求结果限流形同虚设。这个要根据业务容忍度来决定核心接口宁可失败也不能被打垮非核心接口可以降级放行。4.2 限流误伤的排查思路比限流不生效更头疼的是限流误伤——正常用户被挡住了。这种情况通常有几个原因KeyResolver粒度太粗。比如用IP限流结果一个公司出口IP下的几百个用户共享一个桶一个人刷接口全公司遭殃。解决办法是细化到用户维度或者用IP用户组合。阈值设置过低。这个好判断看监控里被限流的请求占比如果超过1%就说明阈值可能偏低了。但也不能盲目调高得结合后端服务的实际承载能力。令牌桶容量太小。有些接口天然有突发特性比如首页加载会同时发好几个请求如果burstCapacity设成和replenishRate一样突发请求就会被限。这种情况把burstCapacity调大就行。时间窗口对齐问题。RedisRateLimiter是基于时间戳计算的如果多个Gateway实例的服务器时间不一致可能导致令牌计算出现偏差。生产环境一定要开启NTP时间同步。4.3 高并发下限流器的性能优化限流器本身也是要消耗资源的如果限流逻辑成为瓶颈那就本末倒置了。几个优化点减少Redis交互次数。每次请求都调一次RedisQPS上万时Redis压力很大。可以考虑本地缓存Redis的两级限流本地用Guava RateLimiter做粗粒度限流Redis做精确的全局限流。本地限流挡住大部分超量请求只有本地放行的才去查Redis。Lua脚本预热。Redis执行Lua脚本前需要先加载脚本第一次执行会有额外开销。可以在应用启动时用SCRIPT LOAD命令预加载脚本后续用EVALSHA执行省去脚本传输的开销。连接池调优。Lettuce连接池的max-active要足够大否则高并发下获取连接会排队。但也不是越大越好Redis单线程处理命令连接太多反而增加上下文切换开销。一般按并发线程数×1.5来设置。spring: redis: lettuce: pool: max-active: 200 max-idle: 50 min-idle: 20 max-wait: 500ms shutdown-timeout: 200ms注意max-wait不要设太大否则获取连接超时时会阻塞很久。设成500ms左右超时了快速失败比一直等着强。4.4 限流与熔断的配合使用限流和熔断是两件事但经常需要配合使用。限流是主动防御在请求到达前就挡住超量部分熔断是被动保护当后端服务已经出问题时快速失败避免雪崩。Gateway里可以用Resilience4j实现熔断filters: - name: CircuitBreaker args: name: orderServiceCircuitBreaker fallbackUri: forward:/fallback/order - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 500 redis-rate-limiter.burstCapacity: 1000 key-resolver: #{userKeyResolver}顺序上限流过滤器应该在熔断器之前执行。因为限流是入口的第一道防线先把超量请求挡住剩下的请求再走熔断逻辑。如果顺序反了熔断器会对所有请求包括本该被限流的做统计可能误判。熔断的fallback接口也要做好限流保护否则后端服务挂了所有请求都打到fallbackfallback也可能被打垮。我一般给fallback接口单独配一个更宽松的限流规则保证它能扛住全量流量。5. 限流策略的进阶玩法5.1 多级限流组合方案单一维度的限流往往不够用实际项目里我通常会用多级限流组合第一级全局QPS限流。在网关最外层做一个总的QPS限制防止整体流量超过系统承载能力。这个阈值设得比较宽松只挡住极端异常的情况。第二级接口维度限流。针对每个API路径单独限流核心接口严格一点非核心接口宽松一点。第三级用户维度限流。防止单个用户刷接口这个阈值根据用户等级可以差异化配置VIP用户给更高的配额。第四级IP维度限流。防止单个IP的恶意攻击这个通常设得比较宽松只挡住明显的攻击行为。这四级限流通过多个KeyResolver组合实现每个KeyResolver对应一个限流过滤器按顺序执行。任何一级被限流请求都会被拒绝。Configuration public class MultiLevelRateLimiter { Bean public KeyResolver globalKeyResolver() { return exchange - Mono.just(global); } Bean public KeyResolver apiKeyResolver() { return exchange - Mono.just( exchange.getRequest().getPath().value()); } Bean public KeyResolver userKeyResolver() { return exchange - { String userId exchange.getRequest() .getHeaders().getFirst(X-User-Id); return Mono.just(userId ! null ? userId : anonymous); }; } }配置上给同一个路由挂多个RequestRateLimiter过滤器每个用不同的KeyResolver和阈值。注意过滤器的order要设置好全局限流在最前用户限流在最后。5.2 基于业务指标的动态限流固定阈值的限流有个局限它不知道后端服务的实际负载情况。如果后端服务已经快扛不住了但QPS还没到阈值限流器不会介入服务可能被打垮。更好的做法是根据后端服务的实时指标动态调整限流阈值。实现思路是网关定期从监控系统比如Prometheus拉取后端服务的CPU使用率、响应时间等指标当指标超过健康阈值时自动调低限流阈值指标恢复正常后再调回去。Component public class AdaptiveRateLimiter { private final AtomicInteger currentRate new AtomicInteger(500); private final AtomicInteger currentCapacity new AtomicInteger(1000); Scheduled(fixedRate 10000) public void adjustRateLimit() { double cpuUsage getBackendCpuUsage(); double responseTime getBackendResponseTime(); if (cpuUsage 0.8 || responseTime 500) { // 后端压力大降低限流阈值 currentRate.updateAndGet(v - Math.max(100, (int)(v * 0.8))); currentCapacity.updateAndGet(v - Math.max(200, (int)(v * 0.8))); } else if (cpuUsage 0.5 responseTime 200) { // 后端压力小恢复限流阈值 currentRate.updateAndGet(v - Math.min(1000, (int)(v * 1.1))); currentCapacity.updateAndGet(v - Math.min(2000, (int)(v * 1.1))); } } }这个方案的关键是调整要平滑不能一下子从500降到100那样会造成大量请求被拒。每次调整幅度控制在20%以内给系统一个缓冲时间。5.3 限流数据的监控与告警限流配置得再好没有监控也是盲人摸象。必须把限流相关的指标采集起来包括被限流的请求数、限流触发的频率、当前令牌桶的剩余量、Redis的响应时间等。Gateway可以通过Micrometer把指标暴露给PrometheusBean public MeterRegistryCustomizerMeterRegistry rateLimitMetrics() { return registry - { Gauge.builder(gateway.ratelimit.remaining, rateLimiter, r - r.getRemainingTokens()) .register(registry); Counter.builder(gateway.ratelimit.rejected) .register(registry); }; }告警规则我一般设两条一是限流拒绝率超过5%持续1分钟说明阈值可能偏低或者有异常流量二是Redis响应时间超过100ms说明限流器本身可能成为瓶颈。监控大盘上要能直观看到每个接口的限流情况哪个接口被限得最多哪个用户被限得最多这些信息对排查问题非常关键。我见过一个案例某个接口的限流拒绝率突然飙升排查发现是一个爬虫在疯狂抓取通过限流数据很快就定位到了问题源头。5.4 限流规则的灰度发布限流规则变更也是有风险的阈值调小了可能误伤调大了可能限不住。稳妥的做法是灰度发布先在一小部分流量上应用新规则观察一段时间没问题再全量。实现上可以通过请求头里的灰度标记来决定用哪套限流规则Bean public KeyResolver grayKeyResolver() { return exchange - { String grayFlag exchange.getRequest() .getHeaders().getFirst(X-Gray-Flag); String userId exchange.getRequest() .getHeaders().getFirst(X-User-Id); if (true.equals(grayFlag)) { return Mono.just(gray: userId); } return Mono.just(normal: userId); }; }灰度用户用新的限流阈值普通用户用旧的两套规则并行运行。观察灰度用户的限流指标确认新规则没问题后再逐步扩大灰度范围。这套机制在大促前调整限流阈值时特别有用可以先拿1%的流量试试水确认没问题再全量推开避免一刀切导致的事故。6. 个人实操经验与避坑清单限流这件事配置本身不难难的是在各种边界情况下不出问题。我把自己踩过的坑和总结的经验整理成一份清单都是真金白银换来的教训。关于Redis的高可用。限流强依赖RedisRedis挂了限流就失效了。生产环境Redis必须做主从哨兵或者集群不能单点。但要注意Redis主从切换期间会有短暂的数据不一致可能导致限流短暂失效。如果业务绝对不能接受限流失效可以考虑本地缓存Redis的双层方案Redis不可用时降级到本地限流。关于KeyResolver的性能。KeyResolver是在每个请求上执行的如果里面做了复杂的计算或者远程调用会严重影响网关性能。KeyResolver里只做简单的取值和拼接不要做任何IO操作。关于令牌桶的预热。新启动的Gateway实例令牌桶是满的会放行一波突发流量。如果后端服务还没预热好这波流量可能把它打垮。可以在应用启动时先消耗掉一部分令牌或者配置一个预热期令牌生成速率从低到高逐渐增加。关于限流阈值的单位。replenishRate的单位是每秒不是每分钟。我见过有同学按每分钟的QPS来配置结果限流阈值大了60倍完全起不到限流效果。配置前一定要确认单位。关于限流后的重试。被限流的请求返回429客户端不应该立即重试而应该等待一段时间。可以在响应头里加上Retry-After告诉客户端多久后可以重试。前端拿到429后做指数退避重试避免加剧限流。关于压测验证。限流配置上线前一定要压测验证。用JMeter或者wrk模拟高并发观察限流是否按预期生效阈值是否准确被限流的请求比例是否合理。压测时要注意压测机的性能要足够否则压测机先扛不住了测出来的结果不准。关于日志记录。被限流的请求要记录日志包括请求路径、用户ID、IP、时间戳等信息。这些日志在排查问题时非常有用可以快速定位是谁在刷接口、哪个接口被限得最多。但日志量可能很大要做好采样和滚动清理。关于配置的版本管理。限流配置的变更要有记录谁在什么时候改了什么改之前是什么值。出问题时可以快速回滚。我一般把限流配置放在Git里管理每次变更都走代码评审流程。最后说一个我个人的体会限流不是越严格越好而是在保护系统和用户体验之间找平衡。阈值设得太松系统可能被打垮设得太紧正常用户被误伤业务受损。这个平衡点需要通过监控数据不断调整没有一劳永逸的配置。我一般会先设一个相对宽松的阈值上线观察一周的监控数据再根据实际情况逐步收紧直到找到一个既能保护系统又不影响正常业务的平衡点。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑