网关高可用:动态路由与多实例部署
网关是所有流量的总入口,它的高可用直接决定系统可用性。本章讲两件事:路由配置如何动态生效(不重启网关),以及网关多实例如何部署与运维。
网关必须“无状态”
Spring Cloud Gateway 基于 WebFlux,本身不保存会话状态,天然适合水平扩展。多实例部署的前提是:所有实例配置一致、不落本地状态、限流等共享能力依赖外部存储(如 Redis)。网关实例之间无主从、无粘滞会话,前面挂 Nginx/SLB 做负载均衡即可:
upstream gateway-cluster {
server 10.0.1.11:8080;
server 10.0.1.12:8080;
server 10.0.1.13:8080;
}
server { listen 80; location / { proxy_pass http://gateway-cluster; } }
多实例要求“配置同源”:路由规则等全部放到配置中心(Nacos Config),实例启动即拉取,避免每个实例一份手工配置造成漂移。
动态路由:从配置中心驱动
默认路由写在 application.yml,改路由要重启网关,这在生产不可接受。常见做法是让路由定义来自配置中心,变更时自动刷新。
思路:启动时把 Nacos 中的 JSON 路由列表解析为 RouteDefinition 写入网关内存路由表;监听配置变更,变更后重新解析并发布刷新事件。
@Component
@RequiredArgsConstructor
public class NacosDynamicRoutes implements ApplicationRunner {
private final RouteDefinitionWriter writer; // 写入路由定义
private final ApplicationEventPublisher publisher; // 发布刷新事件
private final NacosConfigService configService;
@Override
public void run(ApplicationArguments args) throws Exception {
String dataId = "gateway-routes.json";
String json = configService.getConfig(dataId, "DEFAULT_GROUP", 5000);
refresh(json); // 初次加载
configService.addListener(dataId, "DEFAULT_GROUP",
(cfg, type) -> refresh(cfg)); // 配置变化自动刷新
}
private void refresh(String json) {
// 解析 JSON -> List<RouteDefinition>,逐个写入 writer
// 最后发布事件通知网关重建路由表
publisher.publishEvent(new RefreshRoutesEvent(this));
}
}
JSON 路由示意:
[
{
"id": "order-route",
"uri": "lb://order-service",
"predicates": [{ "name": "Path", "args": { "pattern": "/order/**" } }]
}
]
配合灰度发布(权重路由、版本标识路由见对应章节),路由变更全程无需重启。
健康检查与优雅上下线
- 网关实例暴露
/actuator/health给负载均衡器/容器编排做存活探活。 - 发布新版本时先摘流量、等存量请求结束(优雅停机),再滚动替换实例,避免重启瞬间大量 502。
- 网关自身出问题要有“快速失败”预案:上游全挂时返回兜底错误页/限流提示,而不是让请求无限堆积。
实例内高可用细节
| 环节 | 配置/做法 |
|---|---|
| 超时 | 网关到下游设置连接与响应超时,防止慢下游占满线程 |
| 重试 | 对幂等请求可开重试过滤器,写操作慎开 |
| 限流 | 集中式限流依赖 Redis(RequestRateLimiter),实例间配额共享 |
| 连接池 | 调大 HttpClient 连接池上限,避免高并发下连接排队 |
常见误区:只给网关多副本,却忘记 Redis/注册中心/下游也要多副本,网关扩容后反而把单点压力放大。
小结
网关高可用的核心是“无状态多实例 + 配置同源 + 动态路由刷新 + 健康检查”。把路由下沉到配置中心并实现变更自动刷新,再叠加实例级健康检查与优雅停机,网关就能像普通服务一样滚动发布与扩容。