边界策略的四种基本类型

做系统设计这么多年,边界策略其实就那几类。说白了,就是决定「谁来」、「来多少」、「怎么走」、「顶不住怎么办」。我习惯把这四种策略看作是系统的四道防线,缺一不可。

4.1 访问控制:第一道门禁

访问控制是最基础的策略。它解决的是「你是谁,你能干什么」。我在项目中见过太多把访问控制当成配置文件的,结果漏配一个接口就给线上捅了大篓子。

常用的模型就三种:

  • ACL(访问控制列表):简单粗暴,列一堆黑名单白名单。小系统够用,但别指望它能撑住复杂的权限关系。
  • RBAC(基于角色的访问控制):你设几个角色,比如 admin、operator、viewer,然后把人往里塞。说实话,大部分业务场景用这个就够了。
  • ABAC(基于属性的访问控制):这是高阶玩法。根据用户属性、资源属性、环境属性动态判断。比如「仅限在工作时间访问敏感数据」,这就得靠 ABAC。
我的习惯:能用 RBAC 就别碰 ABAC,后者规则多了维护成本很高。曾有一个项目,ABAC 规则写了两千多条,最后谁都不敢动。

4.2 限流:别让人挤爆大门

限流是保护系统不被突发流量冲垮。你想想看,要是双十一零点一秒进来十万个请求,没限流的话系统直接就挂了。

常见算法有四类:

算法 核心逻辑 适用场景
计数器 固定时间窗口内计数,超了就拒绝 简单场景,但窗口边界有突刺问题
滑动窗口 把窗口切细,滑动判断 比计数器平滑,适合通用限流
令牌桶 固定速率往桶里放令牌,请求来了拿令牌 能应对突发流量,常用于 API 网关
漏桶 请求先进桶,以固定速率漏出 强制削峰,适合保护下游
避坑指南:我曾经在项目里直接用计数器限流,结果在分钟边界上流量直接翻倍。后来换成滑动窗口才稳住。记住,计数器限流只适合 Demo,线上用必须配合滑动窗口。

4.3 路由:流量往哪走

路由策略决定请求去哪台机器、哪个服务。这不是简单的负载均衡,边界路由还要考虑灰度、版本、区域。

我总结了三类核心路由策略:

  1. 静态路由:写死的转发规则。简单可靠,但改一次要重新部署。
  2. 动态路由:根据注册中心实时调整。比如 Consul、Nacos 里的服务列表变了,路由自动跟上。
  3. 条件路由:按 header、cookie、参数等条件分发。灰度发布就靠这个,比如把「userId=1000~2000」的流量切到新版本。

嗯,这里要注意——路由策略和限流策略经常打架。我见过一个案例,路由把流量引到 A 集群,限流在 B 集群生效,结果该限的没限住,直接打崩了。

关键原则:路由和限流必须共享同一个配置中心。两者在网关层就对齐,别让下游服务自己猜。

4.4 熔断:及时止损

熔断是你对外部依赖做的最后一道防护。当一个下游服务连续报错或超时,你不能让它拖死整个调用链。

熔断器有三种状态:

  • 关闭:正常状态,请求正常通过。但内部计数器在统计错误率。
  • 打开:错误率超过阈值,直接拒绝请求。快速失败,不等待。
  • 半开:过一段时间后,放少量请求试试水。成功就关闭,失败继续打开。

我常用的熔断参数是这样的:

参数 推荐值 说明
错误率阈值 50% 过去 10 秒内请求错误超过 50% 就熔断
最小请求数 20 至少统计到 20 个请求才触发判断
熔断时长 30 秒 熔断后等 30 秒再尝试半开
半开最大请求 5 半开状态最多放 5 个请求试探
我曾经踩过的坑:把熔断阈值设得太低,比如 10%。结果一次正常的网络抖动就把所有下游都熔断了,那叫一个惨。建议从 50% 开始,监控稳定后再调低。

四种策略的关系

这四种策略不是孤立的。访问控制在最前面,限流紧跟其后,路由负责分流,熔断兜底。下图展示了它们之间的协作层级:

边界策略协作层次图 访问控制层 你是谁?你能干什么? —— 用户身份 & 权限校验 限流层 来了多少?能不能接? —— 令牌桶 / 滑动窗口 路由层 流到哪儿?谁来处理? —— 灰度 / 区域 / 条件分发 熔断层 下游顶不住怎么办? —— 自动切断 & 快速失败 所有策略必须共享同一个配置中心,否则边界错位

这张图我画过无数次。每次画完都跟团队强调:别只关注某一层,上下层之间的协作才是边界策略的核心。限流失效了路由扛不住,路由乱指了熔断也兜不住,一环扣一环。

我建议:每接入一个新服务,先把这四层策略的边界画清楚。谁负责接入层,谁负责业务层,别等出事了才发现熔断配置跟限流配置不匹配。

策略编排的注意事项

四种策略虽然独立,但它们的优先级和执行顺序会直接影响系统行为。我整理了一个基础编排模板,你可以参考:

# 伪代码:边界策略执行流程
def handle_request(request):
    # 1. 访问控制:先验身份
    if not auth_check(request):
        return 401
    
    # 2. 限流:再查流量
    if rate_limit(request):
        return 429
    
    # 3. 路由:决定去向
    target = route(request)
    
    # 4. 熔断:检查下游健康
    if circuit_breaker(target) == OPEN:
        return 503
    
    # 5. 真正发请求
    response = call(target, request)
    
    # 6. 记录熔断器状态
    circuit_breaker.record(response)
    
    return response

这个顺序我踩过很多坑才定下来。刚开始我把路由放在限流前面,结果限流规则跟路由规则频繁冲突——路由改了目的地,限流计数器却还在统计旧路径。后来干脆固定死这个顺序,再没出过类似问题。

一句话总结:访问控制挡人,限流挡量,路由指向,熔断保底。顺序不能乱,配置不能散。