CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
API 设计实战入门/课时/架构与集成
RawBlame
课时目录12 节

API 设计实战入门

  1. 01API 基础
  2. 02HTTP 协议层
  3. 03REST 资源设计
  4. 04错误处理
  5. 05认证
  6. 06授权模型
  7. 07API 安全
  8. 08API 风格
  9. 09架构与集成
  10. 10测试
  11. 11可观测与合规
  12. 12文档与工具

架构与集成

网关、BFF 与消息队列。

30 分钟

架构与集成

这一课你将能做到

  1. 设计 API 网关层的核心能力:路由、认证、限流、日志、监控
  2. 实现 BFF(Backend for Frontend)模式,为不同客户端定制 API
  3. 在微服务架构中处理服务间通信、熔断、降级、重试

为什么要学

单体应用时代,API 设计就是 Controller 设计。微服务时代,API 要经过网关、跨服务调用、多团队协作。不懂架构,你设计的 API 在分布式环境里会处处碰壁:超时、重试风暴、循环依赖、数据不一致。

先建立图像

典型微服务 API 架构:

┌─────────┐     ┌─────────┐     ┌─────────┐
│  Web 端  │     │  App 端  │     │ 第三方   │
└────┬────┘     └────┬────┘     └────┬────┘
     │               │               │
     └───────────────┼───────────────┘
                     ▼
            ┌─────────────────┐
            │    API 网关      │
            │  (认证/限流/路由) │
            └────────┬────────┘
                     │
        ┌────────────┼────────────┐
        ▼            ▼            ▼
   ┌─────────┐  ┌─────────┐  ┌─────────┐
   │ BFF-Web │  │ BFF-App │  │ BFF-Open│
   └────┬────┘  └────┬────┘  └────┬────┘
        │            │            │
        └────────────┼────────────┘
                     ▼
        ┌────────────────────────┐
        │      微服务集群          │
        │  ┌────┐ ┌────┐ ┌────┐ │
        │  │课程 │ │用户 │ │订单 │ │
        │  │服务 │ │服务 │ │服务 │ │
        │  └──┬─┘ └──┬─┘ └──┬─┘ │
        │     └──────┼──────┘   │
        │         服务间调用      │
        │      (gRPC/REST/MQ)   │
        └────────────────────────┘

跟着做

步骤 1:API 网关核心能力

// 以 Spring Cloud Gateway 为例

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // 课程服务路由
                .route("lesson-service", r -> r
                        .path("/api/lessons/**")
                        .filters(f -> f
                                .stripPrefix(1)           // 去掉 /api 前缀
                                .addRequestHeader("X-Request-Source", "gateway")
                                .circuitBreaker(c -> c
                                        .setName("lessonCB")
                                        .setFallbackUri("forward:/fallback/lessons"))
                                .retry(3)
                        )
                        .uri("lb://lesson-service"))    // 负载均衡到课程服务
                // 用户服务路由
                .route("user-service", r -> r
                        .path("/api/users/**")
                        .filters(f -> f.stripPrefix(1))
                        .uri("lb://user-service"))
                .build();
    }
}
// 网关统一认证过滤器
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String token = exchange.getRequest().getHeaders().getFirst("Authorization");
            if (token == null || !tokenProvider.validateToken(token.replace("Bearer ", ""))) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            // 把用户信息传给下游服务
            Long userId = tokenProvider.getUserId(token);
            ServerHttpRequest request = exchange.getRequest().mutate()
                    .header("X-User-Id", userId.toString())
                    .build();
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
}

步骤 2:BFF 模式

Web 端需要「课程列表 + 分页 + 讲师信息」,App 端只需要「课程列表 + 封面图」。

// BFF-Web:为 Web 端定制
@RestController
@RequestMapping("/bff/web/lessons")
public class WebLessonBffController {

    @GetMapping
    public ApiResponse<Page<WebLessonCard>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "12") int size) {
        // 调用课程服务
        Page<Lesson> lessons = lessonServiceClient.list(page, size);
        // 批量调用讲师服务,组装数据
        List<Long> instructorIds = lessons.getContent().stream()
                .map(Lesson::getInstructorId).collect(Collectors.toList());
        Map<Long, Instructor> instructors = instructorServiceClient.batchGet(instructorIds);
        // 组装 Web 端需要的完整卡片
        Page<WebLessonCard> result = lessons.map(lesson ->
                WebLessonCard.from(lesson, instructors.get(lesson.getInstructorId())));
        return ApiResponse.success(result);
    }
}

// BFF-App:为 App 端定制(更轻量)
@RestController
@RequestMapping("/bff/app/lessons")
public class AppLessonBffController {

    @GetMapping
    public ApiResponse<List<AppLessonItem>> list() {
        // 只返回必要字段,减少传输量
        return ApiResponse.success(lessonServiceClient.listLite());
    }
}

BFF 的价值:前端不直接调微服务,BFF 做「数据聚合 + 裁剪」。Web 和 App 各自有 BFF,互不影响。

步骤 3:服务间通信——REST vs gRPC vs 消息队列

// 方案一:REST(同步,简单)
@Service
public class LessonServiceClient {

    private final RestTemplate restTemplate;

    public Lesson getLesson(Long id) {
        return restTemplate.getForObject(
                "http://lesson-service/api/lessons/" + id, Lesson.class);
    }
}

// 方案二:gRPC(同步,高性能)
@GrpcClient("lesson-service")
private LessonServiceGrpc.LessonServiceBlockingStub lessonServiceStub;

public Lesson getLesson(Long id) {
    LessonResponse response = lessonServiceStub.getLesson(
            LessonRequest.newBuilder().setId(id).build());
    return Lesson.from(response);
}

// 方案三:消息队列(异步,解耦)
@Service
public class LessonEventPublisher {

    private final KafkaTemplate<String, Object> kafkaTemplate;

    public void publishLessonCreated(Lesson lesson) {
        kafkaTemplate.send("lesson-events", LessonCreatedEvent.from(lesson));
    }
}

@KafkaListener(topics = "lesson-events")
public void handleLessonCreated(LessonCreatedEvent event) {
    // 搜索服务更新索引、统计服务更新计数
}

选型建议:

  • 查询、需要实时响应 → REST/gRPC
  • 写操作、可以异步 → 消息队列
  • 高频内部调用 → gRPC
  • 跨团队、需要文档 → REST

步骤 4:熔断与降级

// Resilience4j 熔断器
@CircuitBreaker(name = "lessonService", fallbackMethod = "getLessonFallback")
@Retry(name = "lessonService")
@Bulkhead(name = "lessonService")
public Lesson getLesson(Long id) {
    return lessonServiceClient.getLesson(id);
}

// 降级方法:返回缓存或默认值
public Lesson getLessonFallback(Long id, Exception e) {
    log.warn("课程服务降级,id={}", id, e);
    return lessonCache.get(id)
            .orElse(Lesson.builder()
                    .id(id)
                    .title("课程加载中")
                    .build());
}
# application.yml
resilience4j:
  circuitbreaker:
    instances:
      lessonService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3

步骤 5:分布式追踪

// Sleuth + Zipkin 自动埋点
// 每个请求生成 traceId,贯穿所有微服务

// 日志输出示例:
// [lesson-service, traceId=abc123, spanId=def456] 查询课程 id=123
// [user-service,   traceId=abc123, spanId=ghi789] 查询用户 id=456
// [order-service,  traceId=abc123, spanId=jkl012] 创建订单

讲清楚要点

  1. API 网关是微服务的门面:统一入口、统一认证、统一限流、统一监控。没有网关,每个服务都要重复造轮子。
  2. BFF 是前端的最佳拍档:不同客户端(Web、App、小程序)需求不同,BFF 为每类客户端定制 API,避免「一个接口满足所有端」的妥协。
  3. 同步调用 vs 异步消息:同步简单但耦合,异步解耦但复杂。读多写少用同步,写多读少用异步,最终一致性场景用消息队列。
  4. 熔断是自我保护:下游服务挂了,熔断器快速失败,避免线程池打满。降级是用户体验的底线:返回缓存、默认值、或友好提示。
  5. 分布式追踪是排障利器:一个请求跨 5 个服务,没有 traceId 就像盲人摸象。Sleuth + Zipkin 或 SkyWalking 是标配。

你可能会懵的地方

  • API 网关和 Nginx 有什么区别? Nginx 是「流量网关」,做负载均衡、SSL 终止、静态资源;API 网关是「业务网关」,做认证、限流、协议转换、聚合。通常 Nginx 在最外层,API 网关在 Nginx 后面。

  • BFF 和 API 网关有什么区别? BFF 是「为特定前端定制数据」,API 网关是「为所有请求提供通用能力」。BFF 可以放在网关后面,也可以独立部署。复杂场景两者并存。

  • 微服务之间用 REST 还是 gRPC? 跨团队、需要文档 → REST;团队内部、高频调用 → gRPC。没有绝对,很多团队混用:对外 REST,对内 gRPC。

  • 消息队列选型:Kafka vs RabbitMQ? Kafka:高吞吐、持久化、流处理,适合日志、事件流;RabbitMQ:低延迟、灵活路由、任务队列,适合业务消息。大数据场景 Kafka,业务解耦 RabbitMQ。

  • 分布式事务怎么做? 尽量避免。如果必须:Saga(补偿模式)、TCC(Try-Confirm-Cancel)、Seata。能用最终一致性就不用强一致性。

错法对照

错法问题正解
所有微服务直接暴露给前端前端要维护多个地址,认证分散API 网关统一入口
一个通用接口给所有端用Web 端数据冗余,App 端流量浪费BFF 按端定制
服务间循环依赖A 调 B,B 调 A,部署死锁梳理依赖,引入消息队列解耦
无熔断直接调用下游下游挂了,上游线程池打满熔断器 + 降级策略
重试无幂等保障网络超时重试,重复创建数据幂等设计 + 重试次数限制

卡点急救

  1. 网关路由不生效:检查 lb:// 服务名是否注册到注册中心(Nacos/Eureka),以及路由规则优先级。
  2. 服务间调用超时:设置合理的超时时间(连接 3s,读取 10s),配置重试但不超过 3 次,避免重试风暴。
  3. 消息队列积压:消费者能力不足。增加消费者实例、批量消费、异步处理。监控 Lag 指标。

面试常问

  • API 网关的核心功能? 路由转发、负载均衡、认证鉴权、限流熔断、日志监控、协议转换、缓存。能讲出每个功能的作用和实现原理。

  • BFF 模式解决什么问题? 不同客户端(Web/App/小程序)对 API 的需求不同,通用 API 会导致数据冗余或不足。BFF 为每类客户端定制聚合接口,前端体验更好,后端微服务保持纯净。

  • 微服务之间调用失败怎么办? 超时设置、重试(幂等前提下)、熔断(防止雪崩)、降级(返回兜底数据)、异步消息(最终一致性)。能讲出完整的服务治理链路。

自测三问

  1. 如果课程服务和用户服务都挂了,订单服务怎么保证「创建订单」接口可用?
  2. 设计一个「App 端首页」的 BFF 接口,需要聚合哪些数据?
  3. 为什么消息队列能削峰填谷?什么场景不适合用消息队列?

小结一句话

架构与集成的核心是「解耦与治理」:API 网关统一入口,BFF 定制前端体验,消息队列异步解耦,熔断降级保障稳定。

延伸

  • Spring Cloud Gateway 文档
  • Resilience4j 文档
  • 微服务设计模式
  • 下一课:测试——契约测试、负载测试与 Mock 策略。
加载本节练习…
← 上一课API 风格下一课 →测试