微服务架构设计模式系列:通信篇之 API Gateway 模式

1. 为什么需要 API Gateway?

在微服务架构下,客户端要调用的服务越来越多:

  • 移动端需要调用 用户服务订单服务支付服务
  • Web 前端需要调用 商品服务库存服务

如果客户端 直接和所有微服务交互,会带来问题:

  • 客户端必须知道所有服务的地址和调用方式
  • 认证、限流、监控等逻辑需要在每个服务里重复实现
  • 升级接口时容易破坏客户端

API Gateway 就是为了解决这些问题:它作为 统一入口,对外屏蔽后端复杂性。


2. API Gateway 的职责

  • 请求路由:根据 URL/规则,把请求转发到对应的微服务
  • 聚合接口:将多个服务结果合并返回(减少客户端多次调用)
  • 统一安全:认证、鉴权、Token 校验
  • 流量控制:限流、熔断、降级
  • 监控日志:统一采集请求日志、指标、Trace ID

3. API Gateway 的实现方式

常见的技术栈:

  • Spring Cloud Gateway(Spring Boot 官方方案)
  • Kong / Nginx / Traefik(独立网关)
  • GraphQL Gateway(前后端数据查询优化)

下面我们以 Spring Cloud Gateway 为例。


4. 示例:Spring Cloud Gateway 配置

application.yml

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:8081
          predicates:
            - Path=/users/**
        - id: order-service
          uri: http://localhost:8082
          predicates:
            - Path=/orders/**
      default-filters:
        - AddRequestHeader=X-Request-Source, gateway

📌 说明:

  • routes:定义路由规则
  • predicates:匹配条件(比如路径、请求头、时间段)
  • filters:请求/响应的增强(比如加头、日志、鉴权)

聚合接口示例

比如:客户端需要 获取用户信息 + 用户订单,如果直接调用两个服务,需要请求两次。
通过 Gateway,我们可以在网关层做聚合:

@RestController
@RequestMapping("/api")
public class AggregationController {

    private final WebClient webClient = WebClient.create();

    @GetMapping("/user-orders/{userId}")
    public Mono<Map<String, Object>> getUserAndOrders(@PathVariable Long userId) {
        Mono<User> user = webClient.get().uri("http://localhost:8081/users/{id}", userId)
                                   .retrieve().bodyToMono(User.class);

        Mono<List<Order>> orders = webClient.get().uri("http://localhost:8082/orders/user/{id}", userId)
                                            .retrieve().bodyToFlux(Order.class).collectList();

        return Mono.zip(user, orders)
                   .map(tuple -> Map.of("user", tuple.getT1(), "orders", tuple.getT2()));
    }
}

客户端只需调用一次:

GET http://gateway:8080/api/user-orders/1001

返回的数据同时包含 用户信息和订单列表


5. API Gateway 的优缺点

优点

  • 屏蔽微服务复杂性,对客户端友好
  • 统一安全和流量治理
  • 可以做接口聚合,减少请求次数

缺点

  • 网关本身可能成为 单点瓶颈
  • 增加了一次网络转发(延迟)
  • 需要高可用部署和水平扩展

6. 最佳实践

  • 高可用:网关部署多副本,配合负载均衡
  • 鉴权前置:所有请求先过网关,减少服务端负担
  • 监控埋点:在网关层采集请求日志和指标
  • 灰度发布:通过网关的路由规则实现流量分流

总结
API Gateway 是微服务架构通信层的核心模式,它不仅仅是一个 路由器,更是 安全、流量、聚合、监控的中枢
在实际项目里,常见做法是:

  • 外部请求 → API Gateway
  • 内部服务调用 → 服务发现(如 Eureka、Consul)

基于 Spring Cloud Gateway 的项目实战

🏗️ 一、项目结构

microservices-demo/
├── gateway-service/        # API Gateway
│   ├── src/main/java/com/example/gateway/
│   │   └── GatewayApplication.java
│   └── src/main/resources/application.yml
│
├── user-service/           # 用户服务
│   ├── src/main/java/com/example/user/
│   │   └── UserApplication.java
│   └── src/main/resources/application.yml
│
└── order-service/          # 订单服务
    ├── src/main/java/com/example/order/
    │   └── OrderApplication.java
    └── src/main/resources/application.yml

📦 二、依赖配置

gateway-service/pom.xml
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Spring Cloud Gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
 	<!-- Redis (限流、缓存等需要) -->
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- Actuator for monitoring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

⚙️ 三、网关配置 (application.yml)

gateway-service/src/main/resources/application.yml
server:
  port: 8080   # 网关对外端口

spring:
  application:
    name: gateway-service

  cloud:
    gateway:
      discovery:
        locator:
          enabled: false   # 可以启用服务发现模式(比如配合 Eureka)
      routes:
        - id: user-service
          uri: http://localhost:8081
          predicates:
            - Path=/users/**
          filters:
            - StripPrefix=0    # 1 去掉 /users 前缀,转发到 user-service
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1  # 每秒新增 5 个令牌
                redis-rate-limiter.burstCapacity: 1  # 短时突发最大 10
                key-resolver: "#{@ipKeyResolver}"

        - id: order-service
          uri: http://localhost:8082
          predicates:
            - Path=/orders/**
          filters:
            - StripPrefix=0

      default-filters:
        - AddRequestHeader=X-Request-Source, gateway
  data:
    redis:
      host: localhost
      port: 6379

management:
  endpoints:
    web:
      exposure:
        include: health,info,gateway

📌 配置说明:

  • id:路由的唯一 ID
  • uri:后端服务地址(可以是 http://,也可以是 lb://service-name
  • predicates:路由条件,这里用 Path 匹配 URL
  • filters:请求/响应过滤器(去掉前缀、增加 header 等)
  • default-filters:所有路由生效

⚠️ 注意:
Spring Cloud Gateway 的 RequestRateLimiter 默认使用 Redis 存储令牌桶。
所以需要在 application.yml 里配置 Redis 连接,比如:

spring:
  data:
    redis:
      host: localhost
      port: 6379

📌 运行效果

  1. 所有请求先走 http://localhost:8080(Gateway)。
  2. /users/** → 转发到 userservice (8081)限流 5 req/s,突发 10
  3. /orders/** → 转发到 orderservice (8082)限流 2 req/s,突发 5
  4. 限流策略基于客户端 IP。

🛡️ 四、自定义全局过滤器和RequestRateLimiter

例如我们希望 记录所有请求日志

gateway-service/src/main/java/com/example/gateway/filter/LoggingFilter.java
@Component
public class LoggingFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(LoggingFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        log.info("Incoming request: {}", path);
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1; // 优先级,数字越小越先执行
    }
}
自定义 KeyResolver

默认的 RequestRateLimiter 需要 key-resolver,我们用客户端 IP 作为 key,保证每个 IP 独立限流。

src/main/java/com/example/gateway/config/RateLimiterConfig.java

package com.example.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimiterConfig {

    /**
     * 使用客户端 IP 作为限流 Key
     */
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange ->
                Mono.just(exchange.getRequest()
                                  .getRemoteAddress()
                                  .getAddress()
                                  .getHostAddress());
    }
}
启动类

src/main/java/com/example/gateway/GatewayApplication.java

package com.example.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

👤 五、用户服务配置

user-service/src/main/resources/application.yml
server:
  port: 8081
spring:
  application:
    name: user-service
user-service/src/main/java/com/example/user/UserController.java
@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public Map<String, Object> getUser(@PathVariable Long id) {
        return Map.of("id", id, "name", "User-" + id);
    }
}

📦 六、订单服务配置

order-service/src/main/resources/application.yml
server:
  port: 8082
spring:
  application:
    name: order-service
order-service/src/main/java/com/example/order/OrderController.java
@RestController
@RequestMapping("/orders")
public class OrderController {

    @GetMapping("/user/{userId}")
    public List<Map<String, Object>> getOrders(@PathVariable Long userId) {
        return List.of(
            Map.of("orderId", 1001, "userId", userId, "item", "Laptop"),
            Map.of("orderId", 1002, "userId", userId, "item", "Phone")
        );
    }
}

🔗 七、调用示例

  1. 请求用户信息
GET http://localhost:8080/users/1

➡️ 网关转发到 http://localhost:8081/users/1

  1. 请求订单信息
GET http://localhost:8080/orders/user/1

➡️ 网关转发到 http://localhost:8082/orders/user/1

  1. 统一请求头
X-Request-Source: gateway

default-filters 自动添加。


✅ 至此,我们已经有一个 完整可运行的 API Gateway 配置,包括:

  • gateway-service:路由 & 过滤器
  • user-service:简单用户 API
  • order-service:简单订单 API
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐