Java案例如何实现网关?从零搭建API网关的完整指南
目录导读
-
什么是API网关?为什么要用Java实现?

- 网关的核心作用与常见场景
- Java在网关实现中的优势(生态、性能、可扩展性)
-
网关实现的核心技术栈
- 基于Servlet的Zuul网关(传统方案)
- 基于Reactive WebFlux的Spring Cloud Gateway(响应式方案)
- 高性能网关Netty + 自定义Handler(进阶方案)
-
实战案例:基于Spring Cloud Gateway实现一个简单网关
- 环境准备与依赖配置
- 路由转发与过滤器编写
- 限流、鉴权、日志记录完整示例
-
常见问题与问答环节
- Q1: 网关与服务发现如何整合?
- Q2: 高并发下如何保证网关性能?
- Q3: 网关的异常处理与降级策略?
什么是API网关?为什么要用Java实现?
API网关是微服务架构中处于客户端与服务端之间的“入口守护者”,它负责统一认证、限流、路由、日志、协议转换等横切关注点,如果没有网关,每个微服务都需要单独处理认证、日志、限流,导致重复代码和运维灾难。
Java实现网关的三大核心原因:
- 生态成熟:Spring全家桶、Netty、Nacos等组件可直接集成。
- 异步非阻塞:基于Netty的响应式网关(如Spring Cloud Gateway)天然支持高并发。
- 定制性强:通过过滤器链(Filter Chain)可灵活插入业务逻辑,如黑名单、IP限流、灰度发布。
网关实现的核心技术栈
1 传统方案:Zuul 1.x(阻塞式I/O)
- 基于Servlet,每个请求占用一个线程,并发受限于线程池。
- 适用于中小规模项目,但高并发下性能瓶颈明显。
- 停更警告:Zuul 2.x已推出但未广泛使用,官方更推荐Spring Cloud Gateway。
2 主流方案:Spring Cloud Gateway(响应式)
- 基于Spring WebFlux + Reactor + Netty,完全异步非阻塞。
- 内置路由、断言(Predicate)、过滤器(Filter)三大核心。
- 性能数据:在相同硬件下,吞吐量是Zuul 1.x的3-5倍。
3 高性能方案:Netty + 自定义Handler
- 适合需要极致性能或定制协议的场景(如IoT网关、游戏网关)。
- 复杂度高,需自行处理编解码、路由表、连接管理等。
- 推荐组合:Netty + Protobuf + Redis共享路由配置。
实战案例:基于Spring Cloud Gateway实现一个简单网关
1 项目环境搭建
<!-- pom.xml 核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.0.1</version>
</dependency>
<!-- 若要整合服务发现,加入Nacos或Eureka客户端 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
2 路由转发配置(YAML方式)
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # 负载均衡到服务名
predicates:
- Path=/api/user/** # 匹配路径
filters:
- StripPrefix=1 # 移除路径前缀
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
3 自定义过滤器:JWT鉴权与日志
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求头中的Token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
// 2. 校验Token(调用Redis或JWT解析)
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 3. 将解析后的用户信息放入请求上下文
String userId = parseUserIdFromToken(token);
exchange.getAttributes().put("userId", userId);
// 4. 记录访问日志(异步写入ES或数据库)
log.info("Request: {} from userId={}", exchange.getRequest().getPath(), userId);
return chain.filter(exchange);
}
}
4 限流与降级(基于Redis)
@Bean
public KeyResolver userKeyResolver() {
// 按用户ID限流(需从请求头或参数获取)
return exchange -> {
String userId = exchange.getAttribute("userId");
return Mono.just(userId != null ? userId : "anonymous");
};
}
启动效果:当/api/user/**路径在1秒内超过10次请求,直接返回429 Too Many Requests,配合Hystrix或Sentinel可实现降级返回默认数据。
常见问题与问答环节
Q1: 网关如何与服务发现(Nacos/Eureka)整合?
A: 配置uri: lb://service-name即可,网关自动从注册中心获取服务实例列表,然后通过负载均衡(默认轮询)转发请求,需在pom.xml引入spring-cloud-starter-loadbalancer。
Q2: 高并发下如何保证网关不成为瓶颈?
A: 首先优先选择响应式网关(如Spring Cloud Gateway),避免线程阻塞;其次开启连接池复用(Netty默认支持)、响应缓存(如CacheFilter)、异步日志(通过MQ写入而非同步IO),CPU密集场景可配置bootstrap线程数。
Q3: 网关出现故障时如何做到服务降级?
A: 使用Sentinel或Hystrix包裹路由实现熔断降级,例如配置fallbackUri: forward:/fallback/default,当后端服务超时或出错时,网关返回预定义的JSON数据(如{"code":503,"message":"服务暂不可用"}),注意降级逻辑本身不能依赖外部服务。
总结与扩展建议
Java实现网关的最佳实践是:选择Spring Cloud Gateway作为基础框架,按需定制过滤器,并整合Sentinel用于限流降级,对于中小项目,上述案例已足够;对于超大规模系统(百万级QPS),可考虑自研基于Netty + 协程(Quasar/Project Loom)的网关,但这要求团队有深厚的Java异步编程经验。
最后强调:不要在网关层处理复杂业务逻辑,它只是一个“透明代理”;认证、限流、日志这些跨横切面功能才是网关真正的价值所在。