动态路由的好处
网关动态路可以从redis中取,或者nacos都可以。好处有:修了配置能够动态更新
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos-->
<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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.7</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置类
这里可以从nacos注入,我是直接写死了
@Configuration
public class DynamicRouteConfig {
public static final String NACOS_SERVICE_ADDR="localhost:8848";
public static final String NACOS_SERVICE_NAMESPACE="7990000d-2835-42d9-86a6-76d998d8c5ea";
public static final String NACOS_ROUTE_DATA_ID="route.json";
public static final String NACOS_SROUTE_GROUP="DEFAULT_GROUP";
}
监听类与初始化
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
@Service
@Slf4j
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
@Resource
private RouteDefinitionWriter routeDefinitionWriter;
@Resource
private RouteDefinitionLocator routeDefinitionLocator;
@Resource
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher =applicationEventPublisher;
}
public void delete(String id){
try {
log.info("gateway delete route id {}",id);
this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}catch (Exception e){
}
}
public void updateList(List<RouteDefinition> definitions){
log.info("gateway update route {}",definitions);
List<RouteDefinition> routeDefinitionsExits = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
if (!CollectionUtils.isEmpty(routeDefinitionsExits)){
routeDefinitionsExits.forEach(routeDefinition ->{
log.info("delete routeDefinition:{}",routeDefinition);
delete(routeDefinition.getId());
});
}
definitions.forEach(this::updateById);
}
private void updateById(RouteDefinition definition) {
try {
log.info("gateway update route {}",definition);
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
}catch (Exception e) {
definition.getId();
return;
}
try{
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}catch (Exception e){
}
}
public String addRoute(RouteDefinition definition){
log.info("gateway add route {}",definition);
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
}
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;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
@Component
@Slf4j
@DependsOn("dynamicRouteConfig")
public class DynamicRouteServiceImolByNacos {
@Resource
private DynamicRouteServiceImpl dynamicRouteService;
private ConfigService configService;
@PostConstruct
public void init(){
log.info("gateway route init......");
try {
configService=initConfigService();
if (configService == null){
log.info("gateway initConfigService fail");
return;
}
String configInfo = configService.getConfig(DynamicRouteConfig.NACOS_ROUTE_DATA_ID,
DynamicRouteConfig.NACOS_SROUTE_GROUP, 3000);
if (StringUtils.isEmpty(configInfo)) {
throw new RuntimeException("ddd");
}
log.info("获取网关配置:\r\n{}",configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
for (RouteDefinition definition : definitionList) {
log.info("update route :{}",definition.toString());
dynamicRouteService.addRoute(definition);
}
}catch (Exception e) {
log.error("初始化网关路由时失败:",e);
}
dynamicRouteByNacosListener(DynamicRouteConfig.NACOS_ROUTE_DATA_ID,
DynamicRouteConfig.NACOS_SROUTE_GROUP);
}
private void dynamicRouteByNacosListener(String dataId, String group) {
try {
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
log.info("getExecutor \r\n");
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
log.info("进行网关更新:\n\r{}",configInfo);
List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
log.info("update route :{}",definitionList.toString());
dynamicRouteService.updateList(definitionList);
}
});
} catch (NacosException e) {
log.error("从nacos 接受动态路由配置出错!!!:",e);
}
}
private ConfigService initConfigService(){
try {
Properties properties = new Properties();
properties.setProperty("serverAddr",DynamicRouteConfig.NACOS_SERVICE_ADDR);
properties.setProperty("namespace",DynamicRouteConfig.NACOS_SERVICE_NAMESPACE);
return NacosFactory.createConfigService(properties);
}catch (Exception e){
log.error("初始化网关路由时失败:",e);
return null;
}
}
}
效果
当nacos 路由文件修改之后,这里可以监听到。
总结
1-要想真正了解,需要了解Gateway源码。
2-了解一些,SPring 的反应式编程