1. 当微服务遇到网关:为什么需要这道门卫?

小区有门禁系统管控人员进出,微服务架构中的网关同样承担着"智能门卫"的角色。当我们的服务从单体架构拆分为十几个微服务后,每个服务直接暴露端口的方式会产生两个严重问题:

  1. 客户端需要记住每个服务的地址和端口(类似记住每户业主的门牌号)
  2. 无法统一实施安全策略(比如每个门卫岗各自为政)

假设我们有一个电商系统包含:

  • 用户服务(8081)
  • 商品服务(8082)
  • 订单服务(8083)

未经网关的调用方式:

// 前端直接调用三个服务(潜在安全隐患)
const userAPI = 'http://host:8081/api/users';
const productAPI = 'http://host:8082/api/products'; 
const orderAPI = 'http://host:8083/api/orders';

引入网关后的变化:

// 所有请求经过统一入口
const gateway = 'http://gateway:8888';

// 请求路径转换为
const userAPI = `${gateway}/user-service/api/users`;
const productAPI = `${gateway}/product-service/api/products`;
const orderAPI = `${gateway}/order-service/api/orders`;

2. 路由配置实战:给请求指路的交通警察

2.1 Spring Cloud Gateway路由示例(技术栈:Spring Cloud 2022.0.4)

spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Color, blue
        
        - id: cache_route
          uri: https://cache.api
          predicates:
            - Method=GET
            - Header=X-Env, internal
            - After=2023-01-20T00:00:00.000+08:00
          filters:
            - RewritePath=/cache/(?<segment>.*), /$\{segment}

配置解读:

  • StripPrefix=1 移除路径中的服务名前缀
  • AddRequestHeader 实现请求染色
  • RewritePath 支持正则表达式改写路径

2.2 Zuul路由配置对比(技术栈:Spring Cloud Netflix Zuul 2.1.6)

@Configuration
public class ZuulConfig {
    
    @Bean
    public ZuulFilter preFilter() {
        return new ZuulFilter() {
            @Override
            public String filterType() {
                return "pre";
            }

            @Override
            public int filterOrder() {
                return 1;
            }

            @Override
            public boolean shouldFilter() {
                return true;
            }

            @Override
            public Object run() {
                RequestContext ctx = RequestContext.getCurrentContext();
                ctx.addZuulRequestHeader("X-Trace-ID", UUID.randomUUID().toString());
                return null;
            }
        };
    }
}

路由规则配置:

zuul.routes.user-service.path=/user/**
zuul.routes.user-service.serviceId=user-service
zuul.routes.user-service.stripPrefix=true

3. 过滤器开发:网关的瑞士军刀

3.1 流量染色过滤器(Spring Cloud Gateway)

public class TrafficMarkerFilter implements GlobalFilter {
    
    // 实现权重分流逻辑
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求头参数
        String clientType = exchange.getRequest()
                .getHeaders()
                .getFirst("X-Client-Type");

        // 添加染色标记
        if ("mobile".equalsIgnoreCase(clientType)) {
            exchange.getRequest().mutate()
                    .header("X-Traffic-Tag", "mobile_group")
                    .build();
        } else {
            exchange.getRequest().mutate()
                    .header("X-Traffic-Tag", "web_group")
                    .build();
        }

        // 流量权重分流示例
        Random random = new Random();
        if (random.nextInt(100) < 20) { // 20%流量
            exchange.getResponse().getHeaders()
                    .add("X-Testing-Env", "canary");
        }
        
        return chain.filter(exchange);
    }
}

3.2 请求校验过滤器(Zuul)

public class AuthFilter extends ZuulFilter {

    // 实现API签名验证
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        String signature = request.getHeader("X-Signature");
        String timestamp = request.getHeader("X-Timestamp");
        
        // 校验签名有效性
        if (!validateSignature(signature, timestamp)) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("Invalid API signature");
        }
        return null;
    }

    private boolean validateSignature(String sign, String timestamp) {
        // 具体签名算法实现
        return SecurityUtils.check(sign, timestamp);
    }
}

4. 这对表兄弟的差异在哪?

4.1 架构设计对比

维度 Spring Cloud Gateway Zuul
通信模型 非阻塞Reactor 同步阻塞
性能基准 RPS 3万+ RPS 1万
配置方式 代码/配置文件 配置文件/注解
依赖组件 WebFlux Servlet
监控集成 Micrometer Hystrix

4.2 性能测试数据对比

压测环境:4核8G服务器,500并发持续请求

指标 Gateway Zuul1.x
平均响应时间(ms) 23 115
错误率(%) 0.01 0.15
CPU占用率(%) 45 78
内存占用(GB) 1.2 2.5

5. 真实场景中的抉择时刻

5.1 适用Gateway的场景

  • 需要灰度发布的金融系统
  • 直播平台的大规模并发请求
  • 物联网设备的高频心跳检测
  • 需要WebSocket支持的在线游戏

典型案例:某短视频平台使用Gateway实现的AB测试:

public class ABTestingFilter extends AbstractGatewayFilterFactory<Object> {
    
    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            // 根据用户ID哈希分桶
            String userId = getUserId(exchange);
            int bucket = Math.abs(userId.hashCode()) % 100;
            
            if (bucket < 10) { // 10%用户
                exchange.getRequest().mutate()
                        .header("X-Feature-Flag", "new_ui");
            }
            return chain.filter(exchange);
        };
    }
}

5.2 坚守Zuul的场景

  • 遗留系统的渐进式改造
  • 需要精细控制线程的传统架构
  • 依赖Hystrix熔断的存量系统
  • 简单路由需求的内部管理系统

改造示例:某传统ERP系统逐步迁移方案

# 第一阶段:原有服务继续使用Zuul
zuul.routes.legacy.path=/legacy/**
zuul.routes.legacy.url=http://old-system

# 第二阶段:新服务使用Gateway
spring.cloud.gateway.routes[0].uri=lb://new-service

6. 那些年我们踩过的坑

6.1 路由配置三不要

  1. 不要忘记超时设置:
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s
  1. 不要无限制重试:
// 错误示范:可能导致雪崩效应
RetryConfig retryConfig = RetryConfig.custom()
        .maxAttempts(10)
        .build();

// 正确做法:结合熔断机制
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
        .failureRateThreshold(50)
        .waitDurationInOpenState(Duration.ofSeconds(30))
        .build();
  1. 不要忽略路径匹配顺序:
routes:
  - id: specific_route
    uri: lb://special-service
    predicates:
      - Path=/api/v2/special
    
  - id: general_route  # 这个配置必须放在后面
    uri: lb://common-service
    predicates:
      - Path=/api/**

6.2 过滤器开发四原则

  1. 线程安全:避免使用实例变量
  2. 快速失败:复杂操作异步处理
  3. 适度抽象:不要过度设计
  4. 监控埋点:记录执行耗时

正确示例

public class LoggingFilter implements GlobalFilter {
    
    // 使用Mono.fromRunnable实现异步日志
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long startTime = System.currentTimeMillis();
        return chain.filter(exchange)
                .then(Mono.fromRunnable(() -> {
                    long duration = System.currentTimeMillis() - startTime;
                    log.info("请求耗时:{}ms URI:{}", 
                            duration, 
                            exchange.getRequest().getURI());
                }));
    }
}

7. 未来演进风向标

  1. 服务网格化:Istio与网关的协同
  2. 智能化路由:基于机器学习的流量预测
  3. 安全增强:集成Web应用防火墙
  4. 协议扩展:gRPC/RSocket支持深化

趋势示例:AI驱动的动态路由

public class SmartRouter {
    
    @Scheduled(fixedRate = 5000)
    public void updateRoutingRules() {
        // 获取实时指标数据
        ServiceMetrics metrics = metricsService.getCurrentMetrics();
        
        // 基于负载情况动态调整
        if (metrics.getCpuUsage() > 80) {
            gatewayClient.updateRoute(
                RouteDefinitionBuilder()
                    .id("dynamic_route")
                    .uri(getLeastLoadedInstance())
                    .predicates(...)
            );
        }
    }
}