学习笔记—微服务—技术栈实践(4)—网关+Nacos的动态路由

网关的动态路由

  之前已经对网关的简单使用有了一个初步的了解,并且对于配置中心对配置的管理也有了一个简单的了解。

  那么,很显然,靠着网关在配置文件里面配置好的路由路径是比较死板的,动态路由是一个很重要的东西,以此减少对代码的修改。

  Spring Gateway的动态路由功能允许根据特定的规则在运行动态配置路由,而无需重新启动应用,比较常见的方式是通过数据库、配置中心或者API来加载或更新路由规则,于此,介绍以下采用配置中心实现动态路由的方式。

  在该实现中,采用Nacos作为配置中心。

网关 + Nacos配置中心实现的动态路由

  首先,引入nacos配置中心和注册中心的依赖。而后,对于配置文件:

1
2
3
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.discovery.locator.filters[0]=StripPrefix=1

  在这部分中,启用了通过服务发现的方式来动态路由,并将服务ID转化为小写,还应用了StripPrefix 过滤器来去掉路径中的前缀。

  换言之,之前断言中的Path还是http://localhost:19088的话,现在就可以用lb://user-auth-service。

  接着,是对于nacos的一系列配置,不详述。

1
2
3
management.endpoint.health.show-details=always
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=*

  对于如上的配置,与Spring Boot管理端点和监控有关,用于监控的。比如management.endpoint.gateway.enabled=true:启用 Spring Cloud Gateway 的管理端点,可以通过该端点查看网关的路由信息。

  在配置好nacos的相关信息后:

1
2
3
4
spring.cloud.gateway.default-filters[0]=DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedHeaders=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedMethods=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOrigins=*

  这部分配置与 Spring Cloud Gateway 的默认过滤器和跨域资源共享(CORS)设置有关。

  spring.cloud.gateway.default-filters[0]=DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE:该配置确保在响应头中,Access-Control-Allow-Origin 只保留唯一值,避免重复的 CORS 头信息。

  另外几个是CORS的配置。allowedHeaders=*:允许所有请求头。allowedMethods=*:允许所有 HTTP 方法(GET, POST, etc.)。allowedOrigins=*:允许所有来源访问。这些配置确保 Gateway 允许跨域请求,并清理重复的 CORS 响应头。

  网关的路由配置文件为:gateway-router.json。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[{
"filters": [{
"args": {
"parts": "1"
},
"name": "StripPrefix"
}],
"id": "user-auth-service",
"order": 0,
"predicates": [{
"args": {
"pattern": "/auth/**"
},
"name": "Path"
}],
"uri": "lb://user-auth-service"
},
……
]

  这是一个json列表,gateway后续会对其进行解析以实现路由的配置。

  为了实现动态路由,首先创建一个配置类DynamicRouteConfig,该配置类的作用是定义如何从 Nacos 加载路由信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class DynamicRouteConfig {

@Resource
private ApplicationEventPublisher publisher;

@Value("${spring.cloud.nacos.config.router-data-id:gateway-router.json}")
private String routerDataId;

@Configuration
public class NacosDynRoute {

@Resource
private NacosConfigProperties nacosConfigProperties;

@Bean
public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() {
return new NacosRouteDefinitionRepository(routerDataId, publisher, nacosConfigProperties);
}
}
}

  这里,我们定义了一个名为 NacosDynRoute 的内部配置类,注入了 NacosConfigProperties,并创建了 NacosRouteDefinitionRepository 的 Bean。

  NacosRouteDefinitionRepository 是实现 RouteDefinitionRepository 接口的自定义实现,用于从 Nacos 中读取路由配置。该类包含以下主要功能:

  构造函数: 初始化时注册了 Nacos 配置监听器。

1
2
3
4
5
6
public NacosRouteDefinitionRepository(String routerDataId, ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) {
this.routerDataId = routerDataId;
this.publisher = publisher;
this.nacosConfigProperties = nacosConfigProperties;
addListener();
}

  获取路由定义: 从 Nacos 中读取路由配置,解析成 RouteDefinition 列表,并返回 Flux 类型。

1
2
3
4
5
6
7
8
9
10
11
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(routerDataId, nacosConfigProperties.getGroup(), 5000);
List<RouteDefinition> routeDefinitions = getListByStr(content);
return Flux.fromIterable(routeDefinitions);
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(Collections.EMPTY_LIST);
}

  添加 Nacos 配置监听器: 当配置发生变化时,发布 RefreshRoutesEvent 事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(routerDataId, nacosConfigProperties.getGroup(), new Listener() {
@Override
public Executor getExecutor() {
return null;
}

@Override
public void receiveConfigInfo(String configInfo) {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}

  靠着这个监听器,可以监听配置的变化。

  最后,通过:

1
2
3
4
5
6
private List<RouteDefinition> getListByStr(String content) {
if (StringUtils.isNotEmpty(content)) {
return JSONObject.parseArray(content, RouteDefinition.class);
}
return new ArrayList<>(0);
}

  将从 Nacos 获取的 JSON 字符串解析为 RouteDefinition 对象列表。

  至此,Nacos 动态路由实现完成。

  使用 Nacos 动态加载 Spring Cloud Gateway 的路由配置。DynamicRouteConfig 配置类用于定义从 Nacos 加载路由的 Bean,NacosRouteDefinitionRepository 则实现了从 Nacos 获取路由定义的逻辑,并处理了路由配置变更的监听。这样,网关可以根据 Nacos 配置的变化自动刷新路由规则,实现动态路由配置的功能。

  在使用时,只需要在nacos配置中心中配置路由的json文件即可。详细实现可见:https://github.com/gagaducko/springcloud-microservice-security/tree/main/gateway-service


学习笔记—微服务—技术栈实践(4)—网关+Nacos的动态路由
https://gagaducko.github.io/2024/09/10/学习笔记—微服务—技术栈实践-4-—网关-Nacos的动态路由/
作者
gagaduck
发布于
2024年9月10日
许可协议