面试官:微服务网关有什么作用?
候选人:路由转发...
面试官:Zuul和Gateway有什么区别?为什么Gateway性能更好?
候选人:😰💦(这...)
别慌!今天我们深入剖析微服务网关的原理和实战!
🎬 第一章:为什么需要网关?
没有网关的问题
客户端直接调用微服务:
手机App → 订单服务 (http://order.com:8001)
→ 商品服务 (http://product.com:8002)
→ 用户服务 (http://user.com:8003)
问题:
1. 客户端需要记住所有服务地址 ❌
2. 跨域问题(CORS)❌
3. 认证授权分散在各个服务 ❌
4. 限流、熔断难以统一管理 ❌
5. 协议不统一(HTTP、gRPC、WebSocket)❌
有网关的好处
客户端只需要知道网关地址:
手机App → 网关 (http://api.gateway.com)
↓
┌───┴───┬───────┬───────┐
↓ ↓ ↓ ↓
订单服务 商品服务 用户服务 支付服务
好处:
1. 统一入口 ✅
2. 统一认证授权 ✅
3. 统一限流熔断 ✅
4. 协议转换 ✅
5. 负载均衡 ✅
🎭 生活比喻:酒店前台
没有前台(没有网关):
客人直接去找:
- 客房服务:3楼
- 餐饮服务:2楼
- 会议室:5楼
→ 客人要记住所有位置 ❌
有前台(有网关):
客人只需要找前台:
"我要订房" → 前台帮你转接客房服务
"我要订餐" → 前台帮你转接餐饮服务
→ 前台统一管理,客人省心 ✅
前台的工作(网关的功能):
1. 身份验证(查看客人证件)
2. 路由转发(告诉客人去哪里)
3. 限流控制(房间满了不让住)
4. 安全检查(不让坏人进来)
🌸 第二章:Zuul 1.x - Netflix的第一代网关
架构原理
基于Servlet的阻塞式IO:
┌──────────────────────────────────────┐
│ Zuul 1.x │
│ │
│ ┌─────────────────────────────┐ │
│ │ Servlet容器(Tomcat) │ │
│ │ │ │
│ │ 每个请求一个线程 │ │
│ │ └─ 线程1 处理请求1 │ │
│ │ └─ 线程2 处理请求2 │ │
│ │ └─ 线程3 处理请求3 │ │
│ └─────────────────────────────┘ │
│ │
│ Filter链: │
│ PRE → ROUTING → POST → ERROR │
└──────────────────────────────────────┘
问题:
每个请求占用一个线程 → 高并发时线程数暴涨 → 性能瓶颈
核心概念:Filter
// 四种Filter类型
1. PRE Filter:路由前执行(认证、限流)
2. ROUTING Filter:路由时执行(转发请求)
3. POST Filter:路由后执行(添加响应头、日志)
4. ERROR Filter:出错时执行(异常处理)
执行顺序:
请求 → PRE → ROUTING → 后端服务 → POST → 响应
↓ 出错 ↓ 出错
ERROR ←──────────── ERROR
💻 代码示例
// pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
// application.yml
zuul:
routes:
order-service: # 路由名称
path: /order/** # 匹配路径
serviceId: order-service # 目标服务
product-service:
path: /product/**
serviceId: product-service
# 敏感头信息(不转发)
sensitive-headers: Cookie,Set-Cookie
# 超时配置
host:
connect-timeout-millis: 5000
socket-timeout-millis: 60000
// 启动类
@SpringBootApplication
@EnableZuulProxy // 启用Zuul
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
// 自定义Pre Filter:Token验证
@Component
public class TokenFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre"; // PRE类型
}
@Override
public int filterOrder() {
return 1; // 执行顺序
}
@Override
public boolean shouldFilter() {
return true; // 是否执行
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 获取Token
String token = request.getHeader("Authorization");
if (StringUtils.isEmpty(token)) {
// 拦截请求
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("{\"error\":\"未授权\"}");
return null;
}
// 验证Token
if (!validateToken(token)) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(403);
ctx.setResponseBody("{\"error\":\"Token无效\"}");
return null;
}
// 放行
return null;
}
private boolean validateToken(String token) {
// 实际项目中:JWT验证、Redis查询等
return "valid-token".equals(token);
}
}
// 自定义Post Filter:响应日志
@Component
public class ResponseLogFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 100;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
long startTime = (Long) ctx.get("startTime");
long duration = System.currentTimeMillis() - startTime;
log.info("请求路径: {}, 耗时: {}ms",
request.getRequestURI(), duration);
return null;
}
}
⚖️ 优缺点
✅ 优点
- 简单易用:基于Servlet,熟悉
- Spring Cloud集成:无缝集成
- 过滤器扩展:灵活
❌ 缺点
- 性能差:阻塞式IO,一请求一线程
- 已停更:Netflix已放弃维护
- 不支持异步:无法处理长连接
🚀 第三章:Spring Cloud Gateway - 新一代网关
架构原理
基于WebFlux的响应式编程:
┌──────────────────────────────────────┐
│ Spring Cloud Gateway │
│ │
│ ┌─────────────────────────────┐ │
│ │ Netty(非阻塞IO) │ │
│ │ │ │
│ │ 少量线程处理大量请求 │ │
│ │ 线程1 → 请求1,2,3,4... │ │
│ │ 线程2 → 请求5,6,7,8... │ │
│ └─────────────────────────────┘ │
│ │
│ 核心概念: │
│ Route(路由) │
│ Predicate(断言) │
│ Filter(过滤器) │
└──────────────────────────────────────┘
优点:
异步非阻塞 → 高并发 → 性能优秀 ✅
核心概念
1. Route(路由):
- ID:唯一标识
- URI:目标地址
- Predicate:匹配条件
- Filter:过滤器链
2. Predicate(断言):
- Path:路径匹配
- Method:方法匹配
- Header:请求头匹配
- Query:查询参数匹配
- After/Before/Between:时间匹配
3. Filter(过滤器):
- GatewayFilter:单个路由的过滤器
- GlobalFilter:全局过滤器
💻 代码示例
// pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
// application.yml
spring:
cloud:
gateway:
routes:
# 订单服务路由
- id: order-service
uri: lb://order-service # lb表示从注册中心负载均衡
predicates:
- Path=/order/** # 路径断言
- Method=GET,POST # 方法断言
filters:
- StripPrefix=1 # 去掉路径前缀
- AddRequestHeader=X-Request-Source, Gateway # 添加请求头
# 商品服务路由
- id: product-service
uri: lb://product-service
predicates:
- Path=/product/**
- Header=Authorization, Bearer.* # 必须有Authorization头
filters:
- name: RequestRateLimiter # 限流
args:
redis-rate-limiter.replenishRate: 10 # 每秒10个令牌
redis-rate-limiter.burstCapacity: 20 # 桶容量20
# 全局配置
default-filters:
- AddResponseHeader=X-Response-Source, Gateway
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
// 启动类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
// 方式2:Java代码配置路由
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("order-service", r -> r
.path("/order/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Request-Source", "Gateway")
.retry(config -> config
.setRetries(3) // 重试3次
.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)
)
)
.uri("lb://order-service")
)
.build();
}
}
// 自定义全局过滤器:Token验证
@Component
@Order(-1) // 优先级:数字越小越先执行
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 白名单:登录接口不需要Token
String path = request.getURI().getPath();
if (path.startsWith("/login")) {
return chain.filter(exchange);
}
// 获取Token
String token = request.getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token)) {
// 返回401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 验证Token(实际项目中:JWT验证)
if (!validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
// 放行
return chain.filter(exchange);
}
private boolean validateToken(String token) {
// JWT验证逻辑
return true;
}
}
// 自定义过滤器:请求日志
@Component
public class LogFilter implements GlobalFilter, Ordered {
@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;
String path = exchange.getRequest().getURI().getPath();
int statusCode = exchange.getResponse().getStatusCode().value();
log.info("请求路径: {}, 状态码: {}, 耗时: {}ms",
path, statusCode, duration);
}));
}
@Override
public int getOrder() {
return 100; // 执行顺序
}
}
// 自定义限流器KeyResolver
@Bean
public KeyResolver ipKeyResolver() {
// 基于IP限流
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
@Bean
public KeyResolver userKeyResolver() {
// 基于用户ID限流
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("userId")
);
}
高级特性
1️⃣ 熔断降级
// application.yml
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/order # 降级地址
// 降级处理
@RestController
public class FallbackController {
@RequestMapping("/fallback/order")
public Mono<Map<String, Object>> orderFallback() {
return Mono.just(Map.of(
"code", 500,
"message", "订单服务暂时不可用,请稍后再试"
));
}
}
2️⃣ 动态路由
@Service
public class DynamicRouteService {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private ApplicationEventPublisher eventPublisher;
/**
* 动态添加路由
*/
public void addRoute(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
// 发布刷新事件
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* 动态删除路由
*/
public void deleteRoute(String routeId) {
routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
}
⚖️ 优缺点
✅ 优点
- 性能优秀:异步非阻塞,Netty
- Spring生态:无缝集成Spring Cloud
- 功能强大:限流、熔断、重试
- 持续更新:官方维护
❌ 缺点
- 学习曲线:响应式编程较难
- 调试困难:异步代码不好调试
🦁 第四章:Kong - 云原生API网关
架构原理
基于OpenResty(Nginx + Lua):
┌────────────────────────────────────────┐
│ Kong Gateway │
│ │
│ ┌──────────────────────────────────┐ │
│ │ OpenResty (Nginx + Lua) │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Plugin System │ │
│ │ - 认证插件 │ │
│ │ - 限流插件 │ │
│ │ - 日志插件 │ │
│ │ - ... │ │
│ └──────────────────────────────────┘ │
│ │
│ 数据库:PostgreSQL / Cassandra │
└────────────────────────────────────────┘
优点:
Nginx的高性能 + Lua的灵活性
核心概念
1. Service:上游服务
2. Route:路由规则
3. Consumer:消费者(调用方)
4. Plugin:插件(功能扩展)
5. Upstream:负载均衡配置
💻 配置示例
# 1. 创建Service
curl -i -X POST http://localhost:8001/services \
--data name=order-service \
--data url=http://order.service.com:8080
# 2. 创建Route
curl -i -X POST http://localhost:8001/services/order-service/routes \
--data paths[]=/order \
--data methods[]=GET \
--data methods[]=POST
# 3. 添加限流插件
curl -i -X POST http://localhost:8001/services/order-service/plugins \
--data name=rate-limiting \
--data config.minute=100 \
--data config.policy=local
# 4. 添加JWT认证插件
curl -i -X POST http://localhost:8001/services/order-service/plugins \
--data name=jwt
# 5. 创建Consumer
curl -i -X POST http://localhost:8001/consumers \
--data username=user1
# 6. 为Consumer创建JWT凭证
curl -i -X POST http://localhost:8001/consumers/user1/jwt
常用插件
认证类:
- JWT:JWT认证
- OAuth2.0:OAuth2.0认证
- Key Auth:API Key认证
- Basic Auth:基础认证
安全类:
- IP Restriction:IP白名单/黑名单
- CORS:跨域配置
- Bot Detection:机器人检测
流量控制:
- Rate Limiting:限流
- Request Size Limiting:请求大小限制
- Response Rate Limiting:响应限流
日志类:
- File Log:文件日志
- HTTP Log:HTTP日志
- TCP Log:TCP日志
⚖️ 优缺点
✅ 优点
- 性能最强:Nginx+Lua,C语言级性能
- 插件丰富:官方+社区,100+插件
- 多语言:不依赖特定语言
- 云原生:K8s原生支持
❌ 缺点
- 学习成本高:需要懂Nginx、Lua
- 依赖数据库:PostgreSQL/Cassandra
- 调试困难:Lua代码不好调试
- 商业版收费:高级功能需付费
📊 第五章:性能对比
压测数据
测试环境:
- 机器:4核8G
- 后端服务:简单的Hello World
压测结果(QPS):
Kong: 15000 QPS ⭐⭐⭐⭐⭐
Gateway: 10000 QPS ⭐⭐⭐⭐
Zuul 1.x: 2000 QPS ⭐⭐
结论:Kong > Gateway >> Zuul
功能对比
| 功能 | Zuul 1.x | Gateway | Kong |
|---|---|---|---|
| 性能 | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 限流 | 需自己实现 | ✅内置 | ✅插件 |
| 熔断 | 需Hystrix | ✅内置 | ✅插件 |
| 认证 | 需自己实现 | 需自己实现 | ✅插件 |
| 协议 | HTTP | HTTP/WebSocket | HTTP/gRPC/WebSocket |
| 语言 | Java | Java | 任意 |
| 学习曲线 | 简单 | 中等 | 复杂 |
| 维护状态 | 已停更 | 活跃 | 活跃 |
选型建议
Zuul 1.x:不推荐(已停更)
Gateway:
✅ Spring Cloud生态
✅ 中小规模(QPS < 1万)
✅ 团队熟悉Java
Kong:
✅ 超高性能需求(QPS > 1万)
✅ 多语言环境
✅ K8s云原生
✅ 有运维能力
🎓 第六章:面试高分回答
问题:Zuul和Gateway有什么区别?
标准回答:
"Zuul和Gateway都是微服务网关,但底层实现完全不同:
Zuul 1.x:
- 基于Servlet的阻塞式IO
- 一个请求占用一个线程,直到响应
- 性能:约2000 QPS
- 已停止更新
- 适合:老项目维护
Spring Cloud Gateway:
- 基于WebFlux的响应式编程
- 底层是Netty,异步非阻塞
- 少量线程处理大量请求
- 性能:约10000 QPS(5倍于Zuul)
- Spring官方维护
- 适合:新项目首选
我们项目的选择: 从Zuul迁移到了Gateway,原因:
- 性能提升5倍
- 支持WebSocket长连接
- 内置限流、熔断功能
- Spring Cloud官方支持"
常见追问
Q:Gateway为什么性能更好?
A:
1. IO模型:
- Zuul:BIO(阻塞IO),一请求一线程
- Gateway:NIO(非阻塞IO),事件驱动
2. 线程模型:
- Zuul:请求线程 = 业务线程,高并发时线程暴涨
- Gateway:IO线程(少量)+ 业务线程(EventLoop)
3. 底层框架:
- Zuul:Servlet容器(Tomcat)
- Gateway:Netty(高性能网络框架)
🎁 总结
一句话记住
- Zuul:老款汽车,已停产 🚗
- Gateway:新能源车,官方推荐 🚙
- Kong:超级跑车,专业级 🏎️
祝你面试顺利!💪✨