前言
API网关在项目中非常重要。
今天这篇文章跟大家一起聊聊工作最常用的6种网关,希望对你会有所帮助。
一、为什么需要API网关?
有些小伙伴在工作中可能会问:我们的系统直接调用微服务不是更简单吗?
为什么非要引入API网关这个"中间商"呢?
让我们先来看一个实际的例子。
没有网关的微服务困境
- // 前端直接调用多个微服务 - 问题重重
- @RestController
- public class FrontendController {
-
- // 问题1:服务地址硬编码
- @Value("${user.service.url:http://localhost:8081}")
- private String userServiceUrl;
-
- @Value("${order.service.url:http://localhost:8082}")
- private String orderServiceUrl;
-
- @Autowired
- private RestTemplate restTemplate;
-
- @GetMapping("/user-dashboard")
- public UserDashboard getUserDashboard(@RequestHeader("Authorization") String token) {
- // 问题2:每个服务都要重复认证逻辑
- if (!validateToken(token)) {
- throw new UnauthorizedException("Token invalid");
- }
-
- // 问题3:需要手动处理服务间调用顺序
- User user = restTemplate.getForObject(userServiceUrl + "/users/current", User.class);
- List<Order> orders = restTemplate.getForObject(orderServiceUrl + "/orders?userId=" + user.getId(), List.class);
-
- // 问题4:错误处理复杂
- if (user == null || orders == null) {
- throw new ServiceUnavailableException("Backend service unavailable");
- }
-
- return new UserDashboard(user, orders);
- }
-
- // 问题5:重复的认证代码
- private boolean validateToken(String token) {
- // 每个接口都要实现的认证逻辑
- return token != null && token.startsWith("Bearer ");
- }
- }
复制代码 引入网关后的优雅架构
- // 网关统一处理所有横切关注点
- @Configuration
- public class GatewayConfig {
-
- @Bean
- public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
- return builder.routes()
- .route("user_service", r -> r.path("/api/users/**")
- .uri("lb://user-service"))
- .route("order_service", r -> r.path("/api/orders/**")
- .uri("lb://order-service"))
- .route("product_service", r -> r.path("/api/products/**")
- .uri("lb://product-service"))
- .build();
- }
- }
- // 前端只需调用网关
- @RestController
- public class FrontendController {
-
- @Autowired
- private RestTemplate restTemplate;
-
- @GetMapping("/api/user-dashboard")
- public UserDashboard getUserDashboard() {
- // 网关已经处理了认证、路由、负载均衡等问题
- return restTemplate.getForObject("http://gateway/api/users/current/dashboard", UserDashboard.class);
- }
- }
复制代码 API网关的核心价值
让我们通过架构图来理解网关在微服务架构中的关键作用:
网关解决的8大核心问题:
- 统一入口:所有请求都通过网关进入系统
- 认证授权:集中处理身份验证和权限控制
- 流量控制:限流、熔断、降级等 resiliency 模式
- 监控统计:统一的日志、指标收集
- 协议转换:HTTP/1.1、HTTP/2、gRPC 等协议适配
- 缓存加速:响应缓存降低后端压力
- 安全防护:WAF、防爬虫、防重放攻击
- 服务治理:服务发现、负载均衡、路由转发
下面我们一起看看工作中最常见的6种API网关有哪些。
二、Spring Cloud Gateway
有些小伙伴在Spring技术栈中开发微服务,Spring Cloud Gateway 无疑是最自然的选择。
作为Spring官方推出的第二代网关,它基于WebFlux响应式编程模型,性能卓越。
核心架构深度解析
- @Configuration
- public class AdvancedGatewayConfig {
-
- @Bean
- @Order(-1)
- public GlobalFilter customGlobalFilter() {
- return (exchange, chain) -> {
- // 前置处理
- long startTime = System.currentTimeMillis();
- ServerHttpRequest request = exchange.getRequest();
-
- // 添加追踪ID
- String traceId = UUID.randomUUID().toString();
- ServerHttpRequest mutatedRequest = request.mutate()
- .header("X-Trace-Id", traceId)
- .build();
-
- return chain.filter(exchange.mutate().request(mutatedRequest).build())
- .then(Mono.fromRunnable(() -> {
- // 后置处理
- long duration = System.currentTimeMillis() - startTime;
- log.info("Request {} completed in {}ms", traceId, duration);
- }));
- };
- }
-
- @Bean
- public RouteLocator advancedRoutes(RouteLocatorBuilder builder) {
- return builder.routes()
- // 用户服务 - 带熔断和重试
- .route("user_service", r -> r.path("/api/users/**")
- .filters(f -> f
- .circuitBreaker(config -> config
- .setName("userServiceCB")
- .setFallbackUri("forward:/fallback/user-service"))
- .retry(config -> config
- .setRetries(3)
- .setMethods(HttpMethod.GET, HttpMethod.POST)
- .setBackoff(100L, 1000L, 2, true))
- .requestRateLimiter(config -> config
- .setRateLimiter(redisRateLimiter())
- .setKeyResolver(apiKeyResolver()))
- .modifyRequestBody(String.class, String.class,
- (exchange, s) -> Mono.just(validateAndTransform(s))))
- .uri("lb://user-service"))
-
- // 订单服务 - 带JWT认证
- .route("order_service", r -> r.path("/api/orders/**")
- .filters(f -> f
- .filter(jwtAuthenticationFilter())
- .prefixPath("/v1")
- .addResponseHeader("X-API-Version", "1.0"))
- .uri("lb://order-service"))
-
- // 商品服务 - 静态资源缓存
- .route("product_service", r -> r.path("/api/products/**")
- .filters(f -> f
- .dedupeResponseHeader("Cache-Control", "RETAIN_FIRST")
- .setResponseHeader("Cache-Control", "public, max-age=3600"))
- .uri("lb://product-service"))
- .build();
- }
-
- @Bean
- public JwtAuthenticationFilter jwtAuthenticationFilter() {
- return new JwtAuthenticationFilter();
- }
-
- @Bean
- public RedisRateLimiter redisRateLimiter() {
- return new RedisRateLimiter(10, 20);
- }
-
- @Bean
- public KeyResolver apiKeyResolver() {
- return exchange -> {
- String apiKey = exchange.getRequest().getHeaders().getFirst("X-API-Key");
- return Mono.just(Optional.ofNullable(apiKey).orElse("anonymous"));
- };
- }
- }
- // JWT认证过滤器
- @Component
- class JwtAuthenticationFilter implements GatewayFilter {
-
- @Autowired
- private JwtUtil jwtUtil;
-
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- String token = extractToken(exchange.getRequest());
-
- if (token == null) {
- return onError(exchange, "Missing authentication token", HttpStatus.UNAUTHORIZED);
- }
-
- try {
- Claims claims = jwtUtil.parseToken(token);
- String username = claims.getSubject();
-
- // 将用户信息添加到header
- ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
- .header("X-User-Name", username)
- .header("X-User-Roles", String.join(",", claims.get("roles", List.class)))
- .build();
-
- return chain.filter(exchange.mutate().request(mutatedRequest).build());
- } catch (Exception e) {
- return onError(exchange, "Invalid token: " + e.getMessage(), HttpStatus.UNAUTHORIZED);
- }
- }
-
- private String extractToken(ServerHttpRequest request) {
- String bearerToken = request.getHeaders().getFirst("Authorization");
- if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
- return bearerToken.substring(7);
- }
- return null;
- }
-
- private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus status) {
- exchange.getResponse().setStatusCode(status);
- DataBuffer buffer = exchange.getResponse().bufferFactory()
- .wrap(("{"error":"" + err + ""}").getBytes());
- return exchange.getResponse().writeWith(Mono.just(buffer));
- }
- }
复制代码 Spring Cloud Gateway 执行流程
优点:
- 与Spring Cloud生态完美集成
- 基于WebFlux,性能优秀
- 功能丰富,支持过滤器和断言
- 配置灵活,支持代码和配置文件两种方式
缺点:
- 对非Spring技术栈不友好
- 学习曲线相对陡峭
- 依赖Spring Cloud组件
使用场景:
- Spring Cloud微服务架构
- 需要深度定制网关逻辑
- 团队熟悉Spring技术栈
三、Kong:企业级API网关标杆
有些小伙伴在企业级场景中需要更高的性能和更丰富的功能,Kong就是这样一个基于Nginx和OpenResty的高性能API网关。
Kong 配置实战
- # kong.yml - 声明式配置
- _format_version: "2.1"
- _transform: true
- services:
- - name: user-service
- url: http://user-service:8080
- routes:
- - name: user-route
- paths: ["/api/users"]
- strip_path: true
- plugins:
- - name: key-auth
- config:
- key_names: ["apikey"]
- hide_credentials: true
- - name: rate-limiting
- config:
- minute: 10
- policy: redis
- - name: prometheus
- enabled: true
- - name: order-service
- url: http://order-service:8080
- routes:
- - name: order-route
- paths: ["/api/orders"]
- methods: ["GET", "POST", "PUT"]
- plugins:
- - name: cors
- config:
- origins: ["https://example.com"]
- methods: ["GET", "POST", "PUT"]
- headers: ["Accept", "Authorization", "Content-Type"]
- - name: request-transformer
- config:
- add:
- headers: ["X-From-Kong: true"]
- remove:
- headers: ["User-Agent"]
- consumers:
- - username: mobile-app
- keyauth_credentials:
- - key: mobile-key-123
- - username: web-app
- keyauth_credentials:
- - key: web-key-456
- plugins:
- - name: ip-restriction
- config:
- allow: ["192.168.0.0/16", "10.0.0.0/8"]
- - name: correlation-id
- config:
- header_name: "X-Request-ID"
- generator: "uuid"
复制代码 自定义Kong插件开发
- -- kong/plugins/request-validator/handler.lua
- local BasePlugin = require "kong.plugins.base_plugin"
- local cjson = require "cjson"
- local RequestValidator = BasePlugin:extend()
- function RequestValidator:new()
- RequestValidator.super.new(self, "request-validator")
- end
- function RequestValidator:access(conf)
- RequestValidator.super.access(self)
-
- local headers = kong.request.get_headers()
- local method = kong.request.get_method()
- local body = kong.request.get_raw_body()
-
- -- API Key验证
- local api_key = headers["X-API-Key"]
- if not api_key then
- kong.response.exit(401, { message = "Missing API Key" })
- end
-
- -- 验证API Key格式
- if not string.match(api_key, "^%x%x%x%-%x%x%x%-%x%x%x$") then
- kong.response.exit(401, { message = "Invalid API Key format" })
- end
-
- -- 请求体验证
- if method == "POST" or method == "PUT" then
- if not body or body == "" then
- kong.response.exit(400, { message = "Request body is required" })
- end
-
- local ok, json_body = pcall(cjson.decode, body)
- if not ok then
- kong.response.exit(400, { message = "Invalid JSON format" })
- end
-
- -- 业务规则验证
- if json_body.amount and tonumber(json_body.amount) <= 0 then
- kong.response.exit(400, { message = "Amount must be greater than 0" })
- end
- end
-
- -- 添加验证通过标记
- kong.service.request.set_header("X-Request-Validated", "true")
- kong.service.request.set_header("X-API-Key", api_key)
-
- -- 记录审计日志
- kong.log.info("Request validated for API Key: ", api_key)
- end
- return RequestValidator
复制代码 优点:
- 配置热更新,无需重启
- 性能卓越
- 插件生态丰富
- 云原生友好
缺点:
- 相对较新,生态不如Kong成熟
- 依赖etcd
- 学习成本较高
使用场景:
- 云原生环境
- 需要动态配置的场景
- 高性能要求的微服务架构
六、Zuul:Netflix经典网关
有些小伙伴在传统Spring Cloud项目中可能还在使用Zuul,虽然它已被Spring Cloud Gateway取代,但了解其原理仍有价值。
Zuul 过滤器实战
优点:
- 与Netflix集成良好
- 过滤器机制灵活
- 文档资料丰富
缺点:
- 性能较差(阻塞IO)
- 已被Spring Cloud Gateway取代
- 社区活跃度下降
使用场景:
- 遗留Spring Cloud项目
- Netflix技术栈
- 非性能敏感场景
七、Traefik:云原生动态网关
有些小伙伴在容器化环境中需要自动服务发现,Traefik就是为云原生而生的动态网关。
Traefik 配置示例
- # apisix-config.yaml
- routes:
- - uri: /api/users/*
- name: user-service
- methods: [GET, POST, PUT, DELETE]
- upstream:
- type: roundrobin
- nodes:
- user-service-1:8080: 1
- user-service-2:8080: 2
- user-service-3:8080: 1
- plugins:
- proxy-rewrite:
- uri: "/users$1"
- limit-count:
- count: 100
- time_window: 60
- key: remote_addr
- rejected_code: 503
- jwt-auth:
- key: user-service
- secret: my-secret-key
- exp: 86400
- - uri: /api/orders/*
- name: order-service
- upstream:
- type: chash
- key: arg_user_id
- nodes:
- order-service-1:8080: 1
- order-service-2:8080: 1
- plugins:
- cors:
- allow_origins: "https://example.com"
- allow_methods: "GET,POST,PUT,DELETE"
- allow_headers: "*"
- response-rewrite:
- body: '{"code": 0, "message": "success", "data": $body}'
- fault-injection:
- abort:
- http_status: 500
- body: "service unavailable"
- percentage: 5
- - uri: /api/products/*
- name: product-service
- upstream:
- type: roundrobin
- nodes:
- product-service:8080: 1
- plugins:
- proxy-cache:
- cache_key: ["$uri", "$args"]
- cache_zone: disk_cache_one
- cache_ttl: 300
- uri-blocker:
- block_rules: ["^/admin/", ".php$"]
- rejected_code: 403
- # 全局插件
- plugins:
- - name: prometheus
- enable: true
- - name: zipkin
- enable: true
- config:
- endpoint: http://zipkin:9411/api/v2/spans
- sample_ratio: 0.001
复制代码 优点:
- 自动服务发现
- 配置简单
- 云原生友好
- 内置监控和Dashboard
缺点:
- 功能相对简单
- 性能不如Nginx系网关
- 高级功能需要企业版
使用场景:
八、6大网关对比
通过前面的分析,我们现在对这六种API网关有了深入的了解。
让我们通过一个全面的对比来帮助大家做出正确的技术选型。
详细对比表格
特性维度Spring Cloud GatewayKongNginxAPISIXZuulTraefik性能高(WebFlux)极高(Nginx)极高(C)极高(Nginx)中(阻塞IO)中配置方式代码/配置声明式YAML配置文件动态配置代码/配置动态配置服务发现Spring Cloud插件支持需手动配置支持Spring Cloud自动发现K8s支持良好良好需Ingress优秀一般优秀监控MicrometerPrometheus基础监控PrometheusHystrix内置学习曲线中中高低中高中低适用场景Spring Cloud企业级传统架构云原生传统Spring容器化选型决策指南
选择Spring Cloud Gateway当:
- 技术栈以Spring为主
- 需要深度定制网关逻辑
- 已经使用Spring Cloud组件
- 团队熟悉响应式编程
选择Kong当:
- 企业级高并发场景
- 需要丰富插件生态
- 有专业运维团队
- 需要成熟的管理界面
选择Nginx当:
- 性能要求极高
- 场景相对简单
- 团队熟悉Nginx
- 资源受限环境
选择APISIX当:
- 云原生环境
- 需要动态配置
- 追求最新技术
- 高性能要求
选择Zuul当:
- 维护遗留Spring Cloud项目
- Netflix技术栈
- 非性能敏感场景
选择Traefik当:
- 容器化部署
- 需要自动服务发现
- 快速开发部署
- 配置简单要求
总结
通过本文的介绍,我们对6种主流API网关有了全面的认识。
在选择网关时需要考虑以下关键因素:
- 技术栈匹配:选择与团队技术栈最匹配的方案
- 性能要求:根据业务并发量选择性能合适的网关
- 功能需求:评估需要的功能特性,如限流、认证、监控等
- 运维成本:考虑部署、监控、维护的复杂度
- 团队能力:评估团队对网关技术的掌握程度
核心建议
- 新项目优先考虑:Spring Cloud Gateway(Spring技术栈)或 APISIX(云原生)
- 高并发场景:Kong 或 Nginx
- 快速原型:Traefik
- 遗留系统:根据现有技术栈选择
记住,没有最好的网关,只有最合适的网关。
合理的网关选型可以大大提升系统的可维护性、可扩展性和性能表现。
最后说一句(求关注,别白嫖我)
如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。
更多项目实战在我的技术网站:http://www.susan.net.cn/project
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |