Spring Gateway 集成Nacos 实现动态路由配置(动态路由的配置)
通过Spring Gateway 集成Nacos实现配置管理,并且实现动态路由管理。 一、创建test-gateway项目,POM文件如下: 二、创建项目配置文件bootstrap.yml a、test_gateway_commons.yml内容如下: 三、创建网关配置类 GatewayConfig.java 四、创建动态路由服务 五、创建通过Nacos读取动态路由配置服务 六、配置动态路由配置文件gateway_dynamic_router,内容如下: 通过以上步骤就完成Spring Gateway 集成Nacos 实现动态路由配置功能。以后只要通过修改Nacos的配置文件就可以时间服务的动态上下线了。不需要再重启网关了。 <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.golte.parent</groupId>
<artifactId>golte-parent</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.golte.gateway</groupId>
<artifactId>golte-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>golte-gateway</name>
<description>Gateway project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
<alibaba.cloud.version>2.2.3.RELEASE</alibaba.cloud.version>
<fastjson.version>1.2.73</fastjson.version>
</properties>
<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-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- sentinel提供的gataway适配器 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- 对外暴露 Spring Boot 监控指标 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${alibaba.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>test-gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build></project>
server:
port: 80spring:
profiles:
active: dev
application:
name: test-gateway
cloud:
nacos:
config:
namespace: ${spring.profiles.active}
server-addr: http://127.0.0.1:8848
extension-configs[0]:
data-id: test_gateway_commons.yml
group: DEFAULT_GROUP
refresh: true#Nacos动态路由配置,json格式nacos:
gateway:
route:
config:
data-id: gateway_dynamic_router
group: DEFAULT_GROUP
#sentinel 相关配置spring:
cloud:
sentinel:
transport:
dashboard: http://127.0.0.1:8080
port: 8719
scg:
fallback:
mode: response
response-status: 455
response-body: error!
nacos:
discovery:
namespace: dev
server-addr: 127.0.0.1:8848management:
endpoints:
web:
exposure:
include: '*'
import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;@Configurationpublic class GatewayConfig { public static final long DEFAULT_TIMEOUT = 30000; public static String NACOS_SERVER_ADDR; public static String NACOS_NAMESPACE; public static String NACOS_ROUTE_DATA_ID; public static String NACOS_ROUTE_GROUP; @Value("${spring.cloud.nacos.discovery.server-addr}") public void setNacosServerAddr(String nacosServerAddr) {
NACOS_SERVER_ADDR = nacosServerAddr;
} @Value("${spring.cloud.nacos.discovery.namespace}") public void setNacosNamespace(String nacosNamespace) {
NACOS_NAMESPACE = nacosNamespace;
} @Value("${nacos.gateway.route.config.data-id}") public void setNacosRouteDataId(String nacosRouteDataId) {
NACOS_ROUTE_DATA_ID = nacosRouteDataId;
} @Value("${nacos.gateway.route.config.group}") public void setNacosRouteGroup(String nacosRouteGroup) {
NACOS_ROUTE_GROUP = nacosRouteGroup;
}
}
DynamicRouteServiceImpl.javaimport org.springframework.beans.factory.annotation.Autowired;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.Service;import lombok.extern.slf4j.Slf4j;import reactor.core.publisher.Mono;/**
* 动态更新路由网关service
* 1)实现一个Spring提供的事宜推送接口ApplicationEventPublisherAware
* 2)提供动态路由的基础方式,可通过获取bean操作该类的方式。该类提供新增路由、更新路由、删除路由,然后实现公布的功效。
*/@Slf4j@Servicepublic class DynamicRouteServiceImpl implements ApplicationEventPublisherAware { @Autowired
private RouteDefinitionWriter routeDefinitionWriter; /**
* 公布事宜
*/
@Autowired
private ApplicationEventPublisher publisher; @Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher;
} /**
* 删除路由
* @param id
* @return
*/
public String delete(String id) { try {
log.info("gateway delete route id {}",id); this.routeDefinitionWriter.delete(Mono.just(id)); return "delete success";
} catch (Exception e) { return "delete fail";
}
} /**
* 更新路由
* @param definition
* @return
*/
public String update(RouteDefinition definition) { try {
log.info("gateway update route {}",definition); 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) { return "update route fail";
}
} /**
* 增添路由
* @param definition
* @return
*/
public String add(RouteDefinition definition) {
log.info("gateway add route {}",definition);
routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success";
}
}
DynamicRouteServiceImplByNacos.javaimport java.util.List;import java.util.Properties;import java.util.concurrent.Executor;import javax.annotation.PostConstruct;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.gateway.route.RouteDefinition;import org.springframework.context.annotation.DependsOn;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 lombok.extern.slf4j.Slf4j;/**
*
* 通过nacos下发动态路由设置,监听Nacos中gateway-route设置
*
*/@Component@Slf4j@DependsOn({ "gatewayConfig" }) // 依赖于gatewayConfig beanpublic class DynamicRouteServiceImplByNacos { @Autowired
private DynamicRouteServiceImpl dynamicRouteService; private ConfigService configService; @PostConstruct
public void init() {
log.info("gateway route init..."); try {
configService = initConfigService(); if (configService == null) {
log.warn("initConfigService fail"); return;
}
String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID,
GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT);
log.info("获取网关当前设置:\r\n{}", configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); for (RouteDefinition definition : definitionList) {
log.info("update route : {}", definition.toString());
dynamicRouteService.add(definition);
}
} catch (Exception e) {
log.error("初始化网关路由时发生错误", e);
}
dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP);
} /**
* 监听Nacos下发的动态路由设置
*
* @param dataId
* @param group
*/
public void dynamicRouteByNacosListener(String dataId, String group) { try {
configService.addListener(dataId, group, new Listener() { @Override
public void receiveConfigInfo(String configInfo) {
log.info("举行网关更新:\n\r{}", configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); for (RouteDefinition definition : definitionList) {
log.info("update route : {}", definition.toString());
dynamicRouteService.update(definition);
}
} @Override
public Executor getExecutor() {
log.info("getExecutor\n\r"); return null;
}
});
} catch (NacosException e) {
log.error("从nacos吸收动态路由设置失足!!!", e);
}
} /**
* 初始化网关路由 nacos config
*
* @return
*/
private ConfigService initConfigService() { try {
Properties properties = new Properties();
properties.setProperty("serverAddr", GatewayConfig.NACOS_SERVER_ADDR);
properties.setProperty("namespace", GatewayConfig.NACOS_NAMESPACE); return configService = NacosFactory.createConfigService(properties);
} catch (Exception e) {
log.error("初始化网关路由时发生错误", e); return null;
}
}
}
[{ "id": "user-admin-router", "order": 5, "predicates": [{ "args": { "pattern": "/useradmin/**"
}, "name": "Path"
}], "filters":[
{ "name":"StripPrefix", "args":{"_genkey_0":1}
}
], "uri": "lb://test-user-admin"},{ "id": "user-app-router", "order": 4, "predicates": [{ "args": { "pattern": "/users/**"
}, "name": "Path"
}], "filters":[
{ "name":"StripPrefix", "args":{"_genkey_0":1}
}
], "uri": "lb://test-user-app"}]