边界策略的四种基本类型
做系统设计这么多年,边界策略其实就那几类。说白了,就是决定「谁来」、「来多少」、「怎么走」、「顶不住怎么办」。我习惯把这四种策略看作是系统的四道防线,缺一不可。
4.1 访问控制:第一道门禁
访问控制是最基础的策略。它解决的是「你是谁,你能干什么」。我在项目中见过太多把访问控制当成配置文件的,结果漏配一个接口就给线上捅了大篓子。
常用的模型就三种:
- ACL(访问控制列表):简单粗暴,列一堆黑名单白名单。小系统够用,但别指望它能撑住复杂的权限关系。
- RBAC(基于角色的访问控制):你设几个角色,比如 admin、operator、viewer,然后把人往里塞。说实话,大部分业务场景用这个就够了。
- ABAC(基于属性的访问控制):这是高阶玩法。根据用户属性、资源属性、环境属性动态判断。比如「仅限在工作时间访问敏感数据」,这就得靠 ABAC。
我的习惯:能用 RBAC 就别碰 ABAC,后者规则多了维护成本很高。曾有一个项目,ABAC 规则写了两千多条,最后谁都不敢动。
4.2 限流:别让人挤爆大门
限流是保护系统不被突发流量冲垮。你想想看,要是双十一零点一秒进来十万个请求,没限流的话系统直接就挂了。
常见算法有四类:
| 算法 | 核心逻辑 | 适用场景 |
|---|---|---|
| 计数器 | 固定时间窗口内计数,超了就拒绝 | 简单场景,但窗口边界有突刺问题 |
| 滑动窗口 | 把窗口切细,滑动判断 | 比计数器平滑,适合通用限流 |
| 令牌桶 | 固定速率往桶里放令牌,请求来了拿令牌 | 能应对突发流量,常用于 API 网关 |
| 漏桶 | 请求先进桶,以固定速率漏出 | 强制削峰,适合保护下游 |
避坑指南:我曾经在项目里直接用计数器限流,结果在分钟边界上流量直接翻倍。后来换成滑动窗口才稳住。记住,计数器限流只适合 Demo,线上用必须配合滑动窗口。
4.3 路由:流量往哪走
路由策略决定请求去哪台机器、哪个服务。这不是简单的负载均衡,边界路由还要考虑灰度、版本、区域。
我总结了三类核心路由策略:
- 静态路由:写死的转发规则。简单可靠,但改一次要重新部署。
- 动态路由:根据注册中心实时调整。比如 Consul、Nacos 里的服务列表变了,路由自动跟上。
- 条件路由:按 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
这个顺序我踩过很多坑才定下来。刚开始我把路由放在限流前面,结果限流规则跟路由规则频繁冲突——路由改了目的地,限流计数器却还在统计旧路径。后来干脆固定死这个顺序,再没出过类似问题。
一句话总结:访问控制挡人,限流挡量,路由指向,熔断保底。顺序不能乱,配置不能散。