目的:通过nacos配置中心维护gateway的路由配置,并且在nacos上修改配置文件后无需重启就能够立即刷新路由。
1.引入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 用于将yaml配置解析为javabean -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.12.4</version>
</dependency>
2. 编写yaml解析工具类
public class YamlUtil {
/**
* 将yaml字符串转成类对象
* @param yamlStr 字符串
* @param clazz 目标类
* @param <T> 泛型
* @return 目标类
*/
public static <T> T toBean(String yamlStr, Class<T> clazz){
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.findAndRegisterModules();
try {
return mapper.readValue(yamlStr, clazz);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将类对象转yaml字符串
* @param object 对象
* @return yaml字符串
*/
public static String toYaml(Object object){
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.findAndRegisterModules();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper = new ObjectMapper(new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER));
StringWriter stringWriter = new StringWriter();
try {
mapper.writeValue(stringWriter, object);
return stringWriter.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json转为yaml
* json 2 yaml
* @param jsonStr json
* @return yaml
* @throws JsonProcessingException Exception
*/
public static String json2Yaml(String jsonStr) throws JsonProcessingException {
JsonNode jsonNode = new ObjectMapper().readTree(jsonStr);
return new YAMLMapper().writeValueAsString(jsonNode);
}
}
3. 在nacos上创建网关路由规则相关配置文件
gateway-route.yaml
spring:
cloud:
gateway:
routes:
#物联网微服务
- id: iotApplication
uri: lb://iotApplication
predicates:
- Path=/iot/**
filters:
- StripPrefix=1
#数据传输微服务
- id: transferApplication
uri: lb://transferApplication
predicates:
- Path=/transfer/**
filters:
- StripPrefix=1
#测试跳转百度
- id: Baidu
uri: http://www.baidu.com
predicates:
- Path=/baidu/**
filters:
- StripPrefix=1
4. 编写yaml映射类
主要用户将上面的配置文件转为java Bean对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GatewayRouteConfigProperties {
private Spring spring;
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Spring{
private Cloud cloud;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Cloud{
private Gateway gateway;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Gateway{
private List<RouteDefinition> routes;
}
public List<RouteDefinition> getRouteDefinition(){
return spring.cloud.gateway.routes;
};
}
5. 新增配置类,监听nacos配置文件修改网关配置
@Slf4j
@ConditionalOnBean(NacosConfigProperties.class)
@Configuration
public class DynamicRouteConfiguration implements ApplicationEventPublisherAware {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Value("${spring.cloud.gateway.dynamic.data-id:gateway-route}")
private String dataId;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher applicationEventPublisher;
//用于记录加载的配置文件
private static final List<String> ROUTE_LIST = Collections.synchronizedList(new ArrayList<>());
@PostConstruct
public void dynamicRouteByNacosListener() {
try {
ConfigService configService = NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties());
// 程序首次启动, 并加载初始化路由配置
String initConfigInfo = configService.getConfig(dataId, nacosConfigProperties.getGroup(), 5000);
this.addRouteConfig(initConfigInfo);
//添加监听器监听nacos配置文件内的路由变化
configService.addListener(dataId, nacosConfigProperties.getGroup(), new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
refreshRouteConfig(configInfo);
}
@Override
public Executor getExecutor() {return null;}
});
} catch (NacosException e) {
e.printStackTrace();
}
}
private void publish() {
this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 刷新路由配置
* @param configInfo 配置文件字符串, 必须为json array格式
*/
private void refreshRouteConfig(String configInfo) {
clearRoute();
addRouteConfig(configInfo);
}
private void addRouteConfig(String configInfo) {
try {
//将yaml转为java对象
GatewayRouteConfigProperties gatewayRouteConfig = YamlUtil.toBean(configInfo, GatewayRouteConfigProperties.class);
if( gatewayRouteConfig!=null && CollUtil.isNotEmpty( gatewayRouteConfig.getRouteDefinition() ) ){
gatewayRouteConfig.getRouteDefinition().parallelStream().forEach(this::addRoute);
}
publish();
log.info("动态配置路由加载完成 {}", JSONUtil.toJsonStr(gatewayRouteConfig));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 清除路由信息
*/
private void clearRoute() {
ROUTE_LIST.stream().forEach( id ->{
this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
});
ROUTE_LIST.clear();
}
/**
* 添加路由
*/
private void addRoute(RouteDefinition definition) {
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
ROUTE_LIST.add(definition.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
}
大功告成,试试动态修改nacos的配置文件,网关能够立即刷新路由规则,不用每次修改规则都要重启了。