← 返回场景决策

🔗 API与微服务场景决策

什么时候用 / 什么时候不用 / 边界在哪里 — 9 大核心场景全解析

REST vs GraphQL API版本控制 分页方案 限流 认证 服务拆分 网关 熔断降级 配置中心

1. REST vs GraphQL — API 风格决策

REST = 自助餐固定套餐 菜品固定,给什么吃什么,多了浪费
GraphQL = 点菜系统 想吃什么点什么,不多不少刚刚好

📊 REST vs GraphQL 决策表

维度RESTGraphQL
数据获取固定结构(过度获取/获取不足)客户端指定字段(精确获取)
请求次数多资源需要多次请求一次查询获取关联数据
学习曲线
缓存HTTP缓存天然支持需自己实现
性能监控简单N+1问题需DataLoader
文件上传原生支持需额外处理
适合场景CRUD应用、后台管理复杂关联查询、多端适配
推荐度⭐⭐⭐ 大多数场景⭐⭐ 特定场景

2. API 版本控制决策

📊 版本控制方案决策表

方案示例优点缺点推荐度
URL路径/api/v1/users直观,易理解改URL=改路由⭐⭐⭐
请求头Accept: application/vnd.api.v1+jsonURL不变不直观⭐⭐
查询参数/api/users?version=1简单不规范

💻 Spring Boot URL 版本控制

📁 user-service
📂 src/main/java
📂 com.shop.user.controller
📄 UserV1Controller.java
📄 UserV2Controller.java
@RestController @RequestMapping("/api/v1/users") public class UserV1Controller { @GetMapping public List<UserV1DTO> list() { ... } } @RestController @RequestMapping("/api/v2/users") public class UserV2Controller { @GetMapping public List<UserV2DTO> list() { ... } // 新增字段 }
面试金句 URI Path Versioning(/api/v1/ → /api/v2/)是最直观的版本控制策略:通过物理隔离不同版本的 Controller,实现零风险并行——旧客户端调v1不受影响,新客户端调v2享受新字段。配合 @RequestMapping 的路径前缀,Spring MVC 自动路由分发,无需额外框架。

4. 限流 — 流量控制决策

不限流 = 地铁不限客流 所有人都挤进来,站台爆了
限流 = 地铁限流 控制每分钟进站人数,保证系统不崩

📊 限流算法决策表

算法原理优点缺点适合
固定窗口每分钟计数简单窗口边界突刺简单场景
滑动窗口细粒度时间片平滑内存稍大精确限流
令牌桶 ⭐匀速放令牌允许突发流量实现稍复杂通用首选
漏桶匀速流出输出绝对均匀不允许突发严格匀速

💻 Spring Boot + Redis 令牌桶限流

📁 gateway-service
📂 src/main/java
📂 com.shop.gateway.ratelimit
📄 RateLimiter.java
📂 com.shop.gateway.interceptor
📄 RateLimitInterceptor.java
// 使用 Redis + Lua 脚本实现令牌桶 @Component public class RateLimiter { @Autowired private StringRedisTemplate redis; // Lua 脚本保证原子性 private static final String LUA_SCRIPT = """ local key = KEYS[1] local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) local current = tonumber(redis.call('get', key) or '0') if current < limit then redis.call('INCR', key) redis.call('EXPIRE', key, window) return 1 end return 0 """; public boolean isAllowed(String key, int limit, int windowSec) { DefaultRedisScript<Long> script = new DefaultRedisScript<>(); script.setScriptText(LUA_SCRIPT); script.setResultType(Long.class); Long result = redis.execute(script, Collections.singletonList("rate_limit:" + key), String.valueOf(limit), String.valueOf(windowSec)); return result != null && result == 1; } } // 拦截器中使用 @Override public boolean preHandle(HttpServletRequest request, ...) { String clientId = request.getHeader("X-Client-Id"); if (!rateLimiter.isAllowed(clientId, 100, 60)) { response.setStatus(429); // Too Many Requests return false; } return true; }
面试金句 Redis + Lua 限流的关键在于 Lua 脚本的原子性:Redis 单线程执行整个脚本,GET→判断→INCR→EXPIRE 不会被其他请求插入,彻底避免了并发条件下的 TOCTOU(Time-of-Check to Time-of-Use)竞态。返回 429 状态码是 HTTP 标准语义,配合 Retry-After 响应头让客户端知道何时重试。

5. 认证方案决策

📊 认证方案决策表

方案原理优点缺点适合
Session + Cookie服务端存Session简单不支持分布式单体应用
JWTToken 自包含无状态、跨域难以撤销API/SPA
OAuth2授权码模式安全、标准复杂第三方登录
SSO (单点登录)统一认证中心一次登录全通基础设施重企业内部

6. 微服务拆分决策

什么时候拆微服务
  • 团队 10+ 人:需要独立开发部署 — 团队自治
  • 模块独立变化:用户模块和订单模块变化节奏不同 — 独立演进
  • 性能瓶颈不同:搜索模块需要ES,支付模块需要强一致 — 不同技术栈
  • 故障隔离:一个模块挂了不影响其他 — 容错
什么时候不拆(保持单体)
  • 团队 < 5 人:微服务运维成本远大于开发收益
  • 项目初期:业务模式不清晰 — 先跑通再拆
  • 模块强耦合:改一个地方要改三个服务 — 拆了更痛苦
  • 没有 DevOps:没有自动部署/监控/日志 — 微服务就是灾难

📊 微服务拆分原则

原则含义反模式
单一职责一个服务只做一件事用户服务里包含支付逻辑
限界上下文按业务领域拆,不是按技术层拆成"前端服务""数据库服务"
数据独立每个服务有自己的数据库多个服务共享一个数据库
接口契约服务间通过API通信直接访问别人的数据库

7. API 网关决策

网关 = 公司前台 来访先到前台,前台验证、登记、指路
网关的职责
  • 路由:/api/order → 订单服务,/api/user → 用户服务
  • 认证:统一 Token 验证,下游服务不用重复校验
  • 限流:统一限流,保护后端服务
  • 熔断:下游服务不可用时快速返回
  • 日志:统一请求日志、链路追踪
  • 协议转换:HTTP → gRPC、WebSocket → HTTP

📊 网关方案决策表

方案语言特点适合
Spring Cloud GatewayJavaSpring生态、Filter链Spring Cloud全家桶
KongLua/OpenResty高性能、插件丰富多语言微服务
NginxC极致性能、配置简单简单路由+反向代理
EnvoyC++服务网格、xDS动态配置Istio/服务网格

💻 Spring Cloud Gateway 配置

📁 gateway-service
📂 src/main/resources
📄 application.yml
# application.yml spring: cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path=/api/order/** filters: - StripPrefix=1 - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200 - id: user-service uri: lb://user-service predicates: - Path=/api/user/**
面试金句 lb:// 前缀让 Gateway 通过服务发现(Nacos/Eureka)动态解析后端实例地址,实现客户端负载均衡(Ribbon/LoadBalancer);StripPrefix=1 去掉 URL 第一段路径(/api/order → /order),使后端服务无需感知网关路由前缀;RequestRateLimiter 内置 Redis 令牌桶,在网关层统一限流,避免每个微服务重复实现。

8. 熔断降级决策

熔断 = 电路保险丝 电流太大→保险丝断→保护电器不被烧坏
降级 = 改道 主路不通→走备路(返回默认值/缓存/简化功能)

📊 熔断器状态

状态行为转换条件
CLOSED(正常)请求正常通过错误率 < 阈值
OPEN(熔断)直接拒绝/走降级错误率 > 阈值
HALF-OPEN(半开)放少量请求试探超时后自动进入

💻 Sentinel 熔断降级配置

📁 order-service
📂 src/main/java
📂 com.shop.order.service
📄 OrderService.java
📂 com.shop.order.config
📄 pom.xml
// 1. 添加依赖 // spring-cloud-starter-alibaba-sentinel // 2. 定义降级方法 @Service public class OrderService { @SentinelResource( value = "createOrder", fallback = "createOrderFallback", blockHandler = "createOrderBlock" ) public OrderDTO createOrder(CreateOrderReq req) { // 正常业务逻辑 return orderClient.create(req); } // 业务异常降级 public OrderDTO createOrderFallback(CreateOrderReq req, Throwable t) { log.error("创建订单失败", t); return OrderDTO.fail("系统繁忙"); } // 限流/熔断降级 public OrderDTO createOrderBlock(CreateOrderReq req, BlockException e) { return OrderDTO.fail("系统繁忙,请稍后重试"); } } // 3. Sentinel 控制台配置规则 // 慢调用比例 > 50% 且最小请求数 > 10 → 熔断 10 秒
面试金句 fallback vs blockHandler 是 Sentinel 最常考的区分点:fallback 处理业务异常(如 NullPointerException、超时),是"代码层降级";blockHandler 处理Sentinel 规则触发(限流/熔断被拦截),参数必须带 BlockException。两者通过 AOP 代理拦截,方法签名必须与原方法一致(多一个异常参数),否则 Sentinel 反射调用会报错。

9. 配置中心决策

📊 配置中心决策表

方案特点配置推送适合
Nacos配置+注册中心+命名空间✅ 长轮询推送Spring Cloud Alibaba 首选
Apollo权限管理+灰度发布✅ 长连接推送大团队+权限要求高
Spring Cloud ConfigGit存储❌ 需手动刷新简单场景
环境变量K8s ConfigMap需重启容器化部署

💻 Nacos 配置实战

📁 order-service
📂 src/main/resources
📄 bootstrap.yml
📄 application.yml
# bootstrap.yml spring: application: name: order-service cloud: nacos: config: server-addr: nacos:8848 namespace: prod group: DEFAULT_GROUP file-extension: yaml shared-configs: # 共享配置 - data-id: common-redis.yaml refresh: true - data-id: common-database.yaml refresh: true # Nacos 控制台改配置 → 自动推送到所有实例 → @RefreshScope 生效 # 不用重启!
面试金句 bootstrap.yml 优先于 application.yml 加载,用于配置 Nacos 连接信息本身("先有鸡才能有蛋")。Nacos 通过长轮询(Long Polling)实现配置热更新:客户端每隔 30s 向 Nacos 发送请求挂起,配置变更时服务器立即返回,触发 @RefreshScope 的 Bean 重建。shared-configs 实现多服务共享 Redis/DB 连接配置,避免每个微服务重复维护。

🔗 API与微服务场景决策 · 全栈资料库

← 返回场景决策