Spring Cloud 动态路由实现方案

目前Spring Cloud的路由网关方案主要是:Spring Cloud Gateway。默认便有自动更新路由的实现。通过 RouteRefreshListener 检测到更新条件发送 RefreshRoutesEvent 事件实现。更新条件:在服务启动时、新服务注册时、配置中心配置修改时、心跳更新时都会触发。

下面说下自己实现的方案,可以根据自身情况,自定义路由定义实体类。

使用Nacos配置监听方式

思路:监听配置变化,然后 RouteDefinitionWriter 更新路由规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Configuration
public class DynamicRouteConfig {

@Autowired
private RouteDefinitionWriter routeDefinitionWriter;

@Autowired
private ConfigService configService;

@PostConstruct
public void initRoute() throws NacosException {
// 监听Nacos配置变化
configService.addListener("gateway-routes.json", "DEFAULT_GROUP", new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
try {
// 清除现有路由
clearExistingRoutes();

// 解析新的路由配置
List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);

// 添加新的路由
routeDefinitions.forEach(routeDefinition -> {
try {
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
} catch (Exception e) {
log.error("更新路由失败", e);
}
});
} catch (Exception e) {
log.error("路由更新失败", e);
}
}

@Override
public Executor getExecutor() {
return null;
}
});
}

private void clearExistingRoutes() {
// 获取路由管理器
RouteDefinitionLocator routeDefinitionLocator = SpringUtil.getBean(RouteDefinitionLocator.class);

// 删除现有路由
routeDefinitionLocator.getRouteDefinitions()
.subscribe(routeDefinition -> {
try {
routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).subscribe();
} catch (Exception e) {
log.error("删除路由失败", e);
}
});
}
}

使用Spring Cloud Bus实现自动刷新

思路:依赖配置服务后,我们可以 @RefreshScope 决定一个类自动刷新配置,包括类的创建,定义自己的RouteLocator, 每当配置更新时,根据配置重新创建 RouteLocator,更新路由规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RefreshScope
@Configuration
public class GatewayConfig {

@Value("${spring.cloud.gateway.routes}")
private String routesConfig;

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
// 路由配置逻辑
return builder.routes()
// ... 根据routesConfig构建路由
.build();
}
}