一、没有网关的日子我们是怎么过的2018年我们的微服务直接暴露给前端。前端要记10个不同的域名和端口。更痛苦的是每个服务各自实现鉴权、限流、日志代码重复度超过60%。有一次安全审计发现3个服务没有做鉴权2个服务没有做限流。后来我们上了网关统一入口、统一鉴权、统一限流世界瞬间清净了。二、网关核心功能2.1 功能清单┌─────────────────────────────────────────────────────────────────┐ │ 网关核心功能 │ │ │ │ 1. 路由转发 │ │ - 根据路径/域名/Header转发到后端服务 │ │ │ │ 2. 鉴权认证 │ │ - JWT验证、OAuth2认证 │ │ - 统一登录、单点登录 │ │ │ │ 3. 限流熔断 │ │ - 全局限流、按用户限流 │ │ - 服务熔断、降级 │ │ │ │ 4. 协议转换 │ │ - HTTP → gRPC │ │ - HTTP → WebSocket │ │ │ │ 5. 日志监控 │ │ - 请求日志、响应日志 │ │ - 链路追踪 │ │ │ │ 6. 灰度发布 │ │ - 按比例/按用户灰度路由 │ │ │ └──────────────────────────────────────────────────────────────────┘三、Spring Cloud Gateway实现3.1 路由配置# application.ymlspring:cloud:gateway:routes:# 订单服务-id:order-serviceuri:lb://order-servicepredicates:-Path/api/orders/**filters:-StripPrefix1-name:RequestRateLimiterargs:redis-rate-limiter.replenishRate:100redis-rate-limiter.burstCapacity:200key-resolver:#{userKeyResolver}# 商品服务-id:product-serviceuri:lb://product-servicepredicates:-Path/api/products/**filters:-StripPrefix1-name:CircuitBreakerargs:name:productCircuitBreakerfallbackUri:forward:/fallback/product# 支付服务灰度-id:payment-service-v2uri:lb://payment-service-v2predicates:-Path/api/payments/**-HeaderX-Gray,v2filters:-StripPrefix13.2 鉴权过滤器/** * JWT鉴权过滤器 */ComponentSlf4jpublicclassJwtAuthFilterimplementsGlobalFilter,Ordered{AutowiredprivateJwtTokenProvidertokenProvider;/** 白名单路径 */privatestaticfinalSetStringWHITE_LISTSet.of(/api/auth/login,/api/auth/register,/api/auth/refresh,/api/public/**);OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringpathexchange.getRequest().getPath().value();// 白名单放行if(isWhiteListed(path)){returnchain.filter(exchange);}// 获取TokenStringtokenextractToken(exchange.getRequest());if(tokennull){returnunauthorized(exchange,缺少认证Token);}try{// 验证TokenJwtClaimsclaimstokenProvider.validateToken(token);// 将用户信息传递给下游服务ServerHttpRequestrequestexchange.getRequest().mutate().header(X-User-Id,claims.getUserId()).header(X-User-Role,claims.getRole()).header(X-Trace-Id,generateTraceId()).build();returnchain.filter(exchange.mutate().request(request).build());}catch(JwtTokenExpiredExceptione){returnunauthorized(exchange,Token已过期);}catch(JwtTokenInvalidExceptione){returnunauthorized(exchange,Token无效);}}privateMonoVoidunauthorized(ServerWebExchangeexchange,Stringmessage){exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);StringbodyJSON.toJSONString(Result.fail(401,message));DataBufferbufferexchange.getResponse().bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));returnexchange.getResponse().writeWith(Mono.just(buffer));}OverridepublicintgetOrder(){return-100;// 高优先级}}3.3 限流过滤器/** * 自定义限流Key解析器 */ComponentpublicclassUserKeyResolverimplementsKeyResolver{OverridepublicMonoStringresolve(ServerWebExchangeexchange){StringuserIdexchange.getRequest().getHeaders().getFirst(X-User-Id);Stringpathexchange.getRequest().getPath().value();if(userId!null){returnMono.just(rate_limit:userId:path);}// 未登录用户按IP限流Stringipexchange.getRequest().getRemoteAddress().getAddress().getHostAddress();returnMono.just(rate_limit:ip:ip:path);}}3.4 灰度路由/** * 灰度路由过滤器 */ComponentSlf4jpublicclassGrayRouteFilterimplementsGlobalFilter,Ordered{AutowiredprivateGrayConfigServicegrayConfigService;OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){StringuserIdexchange.getRequest().getHeaders().getFirst(X-User-Id);StringserviceIdgetServiceId(exchange);// 检查灰度规则GrayRulerulegrayConfigService.getGrayRule(serviceId);if(rule!nullrule.isEnabled()){booleanisGrayUserisGrayUser(userId,rule);if(isGrayUser){// 灰度用户路由到V2版本StringnewUrirewriteUri(exchange.getRequest().getURI(),rule.getGrayVersion());ServerHttpRequestrequestexchange.getRequest().mutate().uri(URI.create(newUri)).header(X-Gray,true).build();log.info(灰度路由: userId{}, service{}, version{},userId,serviceId,rule.getGrayVersion());returnchain.filter(exchange.mutate().request(request).build());}}returnchain.filter(exchange);}privatebooleanisGrayUser(StringuserId,GrayRulerule){// 按用户ID范围灰度if(rule.getUserIdRange()!null){returnrule.getUserIdRange().contains(Long.parseLong(userId));}// 按百分比灰度if(rule.getPercentage()0){inthashMath.abs(userId.hashCode());returnhash%100rule.getPercentage();}returnfalse;}OverridepublicintgetOrder(){return0;}}四、踩坑实录坑1网关成为单点网关挂了所有服务都不可用。解决网关多实例部署 健康检查 自动扩容。坑2网关性能瓶颈所有请求经过网关QPS高时网关响应慢。解决网关只做轻量操作路由、鉴权重逻辑放在业务服务。坑3网关超时设置不合理网关超时30秒但有些导出接口需要60秒。解决不同路由设置不同超时长连接接口特殊处理。坑4跨域配置遗漏前端请求被CORS策略拦截。解决在网关统一配置CORS。坑5请求体大小限制文件上传请求被网关拒绝因为超过了默认请求体大小限制。解决调整spring.codec.max-in-memory-size配置。五、总结网关设计要点功能方案路由Spring Cloud Gateway鉴权JWT GlobalFilter限流Redis RequestRateLimiter熔断Resilience4J灰度自定义路由规则监控Actuator Prometheus最佳实践网关多实例部署只做轻量操作统一鉴权和限流合理的超时配置完善的监控告警血的教训网关是微服务的大门。门没守好再多的内部安全措施也白搭。思考题你的系统用了什么网关方案有没有踩过坑个人观点仅供参考
【架构实战】网关架构设计:微服务的统一入口
发布时间:2026/6/9 15:32:21
一、没有网关的日子我们是怎么过的2018年我们的微服务直接暴露给前端。前端要记10个不同的域名和端口。更痛苦的是每个服务各自实现鉴权、限流、日志代码重复度超过60%。有一次安全审计发现3个服务没有做鉴权2个服务没有做限流。后来我们上了网关统一入口、统一鉴权、统一限流世界瞬间清净了。二、网关核心功能2.1 功能清单┌─────────────────────────────────────────────────────────────────┐ │ 网关核心功能 │ │ │ │ 1. 路由转发 │ │ - 根据路径/域名/Header转发到后端服务 │ │ │ │ 2. 鉴权认证 │ │ - JWT验证、OAuth2认证 │ │ - 统一登录、单点登录 │ │ │ │ 3. 限流熔断 │ │ - 全局限流、按用户限流 │ │ - 服务熔断、降级 │ │ │ │ 4. 协议转换 │ │ - HTTP → gRPC │ │ - HTTP → WebSocket │ │ │ │ 5. 日志监控 │ │ - 请求日志、响应日志 │ │ - 链路追踪 │ │ │ │ 6. 灰度发布 │ │ - 按比例/按用户灰度路由 │ │ │ └──────────────────────────────────────────────────────────────────┘三、Spring Cloud Gateway实现3.1 路由配置# application.ymlspring:cloud:gateway:routes:# 订单服务-id:order-serviceuri:lb://order-servicepredicates:-Path/api/orders/**filters:-StripPrefix1-name:RequestRateLimiterargs:redis-rate-limiter.replenishRate:100redis-rate-limiter.burstCapacity:200key-resolver:#{userKeyResolver}# 商品服务-id:product-serviceuri:lb://product-servicepredicates:-Path/api/products/**filters:-StripPrefix1-name:CircuitBreakerargs:name:productCircuitBreakerfallbackUri:forward:/fallback/product# 支付服务灰度-id:payment-service-v2uri:lb://payment-service-v2predicates:-Path/api/payments/**-HeaderX-Gray,v2filters:-StripPrefix13.2 鉴权过滤器/** * JWT鉴权过滤器 */ComponentSlf4jpublicclassJwtAuthFilterimplementsGlobalFilter,Ordered{AutowiredprivateJwtTokenProvidertokenProvider;/** 白名单路径 */privatestaticfinalSetStringWHITE_LISTSet.of(/api/auth/login,/api/auth/register,/api/auth/refresh,/api/public/**);OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){Stringpathexchange.getRequest().getPath().value();// 白名单放行if(isWhiteListed(path)){returnchain.filter(exchange);}// 获取TokenStringtokenextractToken(exchange.getRequest());if(tokennull){returnunauthorized(exchange,缺少认证Token);}try{// 验证TokenJwtClaimsclaimstokenProvider.validateToken(token);// 将用户信息传递给下游服务ServerHttpRequestrequestexchange.getRequest().mutate().header(X-User-Id,claims.getUserId()).header(X-User-Role,claims.getRole()).header(X-Trace-Id,generateTraceId()).build();returnchain.filter(exchange.mutate().request(request).build());}catch(JwtTokenExpiredExceptione){returnunauthorized(exchange,Token已过期);}catch(JwtTokenInvalidExceptione){returnunauthorized(exchange,Token无效);}}privateMonoVoidunauthorized(ServerWebExchangeexchange,Stringmessage){exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);StringbodyJSON.toJSONString(Result.fail(401,message));DataBufferbufferexchange.getResponse().bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));returnexchange.getResponse().writeWith(Mono.just(buffer));}OverridepublicintgetOrder(){return-100;// 高优先级}}3.3 限流过滤器/** * 自定义限流Key解析器 */ComponentpublicclassUserKeyResolverimplementsKeyResolver{OverridepublicMonoStringresolve(ServerWebExchangeexchange){StringuserIdexchange.getRequest().getHeaders().getFirst(X-User-Id);Stringpathexchange.getRequest().getPath().value();if(userId!null){returnMono.just(rate_limit:userId:path);}// 未登录用户按IP限流Stringipexchange.getRequest().getRemoteAddress().getAddress().getHostAddress();returnMono.just(rate_limit:ip:ip:path);}}3.4 灰度路由/** * 灰度路由过滤器 */ComponentSlf4jpublicclassGrayRouteFilterimplementsGlobalFilter,Ordered{AutowiredprivateGrayConfigServicegrayConfigService;OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){StringuserIdexchange.getRequest().getHeaders().getFirst(X-User-Id);StringserviceIdgetServiceId(exchange);// 检查灰度规则GrayRulerulegrayConfigService.getGrayRule(serviceId);if(rule!nullrule.isEnabled()){booleanisGrayUserisGrayUser(userId,rule);if(isGrayUser){// 灰度用户路由到V2版本StringnewUrirewriteUri(exchange.getRequest().getURI(),rule.getGrayVersion());ServerHttpRequestrequestexchange.getRequest().mutate().uri(URI.create(newUri)).header(X-Gray,true).build();log.info(灰度路由: userId{}, service{}, version{},userId,serviceId,rule.getGrayVersion());returnchain.filter(exchange.mutate().request(request).build());}}returnchain.filter(exchange);}privatebooleanisGrayUser(StringuserId,GrayRulerule){// 按用户ID范围灰度if(rule.getUserIdRange()!null){returnrule.getUserIdRange().contains(Long.parseLong(userId));}// 按百分比灰度if(rule.getPercentage()0){inthashMath.abs(userId.hashCode());returnhash%100rule.getPercentage();}returnfalse;}OverridepublicintgetOrder(){return0;}}四、踩坑实录坑1网关成为单点网关挂了所有服务都不可用。解决网关多实例部署 健康检查 自动扩容。坑2网关性能瓶颈所有请求经过网关QPS高时网关响应慢。解决网关只做轻量操作路由、鉴权重逻辑放在业务服务。坑3网关超时设置不合理网关超时30秒但有些导出接口需要60秒。解决不同路由设置不同超时长连接接口特殊处理。坑4跨域配置遗漏前端请求被CORS策略拦截。解决在网关统一配置CORS。坑5请求体大小限制文件上传请求被网关拒绝因为超过了默认请求体大小限制。解决调整spring.codec.max-in-memory-size配置。五、总结网关设计要点功能方案路由Spring Cloud Gateway鉴权JWT GlobalFilter限流Redis RequestRateLimiter熔断Resilience4J灰度自定义路由规则监控Actuator Prometheus最佳实践网关多实例部署只做轻量操作统一鉴权和限流合理的超时配置完善的监控告警血的教训网关是微服务的大门。门没守好再多的内部安全措施也白搭。思考题你的系统用了什么网关方案有没有踩过坑个人观点仅供参考