动态路由的实现方式多种多样,研究一下基于 nacos 配置文件形式的动态路由。
1. 创建项目,并pom.xml文件引入如下依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.olive</groupId> <artifactId>olive-gateway</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.1</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.3</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.9</version> </dependency> </dependencies> </project>
2. 增加一个配置类
主要配置 nacos 的 dataId 与 group
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "route.nacos") public class GatewayConfig { private String dataId; private String group; private int timeout; //省略 getter setter }
3.定义监听路由变化类
InFileRouteDefinitionRepository 类主要是简单 nacos 中的配置文件routes.json 的变化;只要监听到 routes.json 就进行路由更新。
import java.util.List; import java.util.concurrent.Executor; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.olive.config.GatewayConfig; import com.olive.route.model.GatewayRouteDO; import reactor.core.publisher.Mono; @Component public class InFileRouteDefinitionRepository implements ApplicationEventPublisherAware{ @Value("${spring.cloud.nacos.discovery.server-addr}") private String nacosServer; @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher publisher; @Autowired private GatewayConfig gatewayConfig; @PostConstruct private void init() { dynamicRouteByListener(gatewayConfig.getDataId(), gatewayConfig.getGroup(), nacosServer, gatewayConfig.getTimeout()); } /** * 监听Nacos Server下发的动态路由配置 */ public void dynamicRouteByListener(String dataId, String group, String nacosServer, int timeout) { try { ConfigService configService = NacosFactory.createConfigService(nacosServer); String content = configService.getConfig(dataId, group, timeout); configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { updateConfig(configInfo); } @Override public Executor getExecutor() { return null; } }); updateConfig(content); } catch (NacosException e) { e.printStackTrace(); } } private void updateConfig(String configInfo) { try { GatewayRouteDO gatewayRouteDO = JSON.parseObject(configInfo, GatewayRouteDO.class); List<RouteDefinition> routeList = gatewayRouteDO.getRoutes(); if (CollectionUtils.isNotEmpty(routeList)) { for (RouteDefinition routeDefinition : routeList) { this.update(routeDefinition); } } } catch (Exception e) { e.printStackTrace(); } } /** * 增加路由 */ public String add(RouteDefinition definition) { routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } /** * 更新路由 */ public String update(RouteDefinition definition) { try { this.routeDefinitionWriter.delete(Mono.just(definition.getId())); } catch (Exception e) { return "update fail, not find route routeId: " + definition.getId(); } try { routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } catch (Exception e) { e.printStackTrace(); return "update route fail"; } } /** * 删除路由 */ public String delete(String id) { try { this.routeDefinitionWriter.delete(Mono.just(id)); return "delete success"; } catch (Exception e) { e.printStackTrace(); return "delete route fail"; } } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } }
4. 增加 application.yml 配置文件
server: port: 8089 spring: application: name: olive-gateway cloud: nacos: discovery: server-addr: 192.168.255.10:8848 route: nacos: dataId: routes.json group: DEFAULT_GROUP timeout: 1000
需要在配置中心 nacos 增加 routes.json 配置文件;这个 json 文件的格式一定要符合 spring-gateway 的 route 格式;否则无法转换。routes.json 内容如下:
{ "routes": [ { "filters": [], "id": "pay_route", "order": 0, "predicates": [ { "args": { "pattern": "/pay/**" }, "name": "Path" } ], "uri": "lb://pay-service" }, { "filters": [ { "name": "RewritePath", "args": { "regexp": "/user/(?<remaining>.*)", "replacement": "/${remaining}" } } ], "id": "user_route", "order": 0, "predicates": [ { "args": { "pattern": "/user/**" }, "name": "Path" } ], "uri": "lb://user-center-service" } ] }
5. 创建springboot 引导类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class GwApplication { public static void main(String[] args) { SpringApplication.run(GwApplication.class, args); } }
测试验证只要通过在配置中心 nacos;修改 routes.json 配置文件即可。