基于nacos实现Spring cloud Gateway实现动态路由的方法

1,008 阅读2分钟

基于Nacos实现Spring Cloud Gateway实现动态路由的方法

简介:该文章主要介绍以Nacos为注册、配置中心,实现Spring Cloud GateWay 动态路由的功能。Spring Cloud Gateway启动时候,就将通过路由配置和规则加载到内存里,无法做到不重启网关就可以动态的对应路由的配置和规则进行增加,修改和删除。通过nacos的配置下发的功能可以实现在不重启网关的情况下,实现动态路由的效果

项目介绍

网关 service-gateway

pom 介绍:

` startagaincloud org.example 1.0 4.0.0

<artifactId>service-gateway</artifactId>

<dependencies>

    <dependency>
        <groupId>org.example</groupId>
        <artifactId>service-common</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

    <!-- 该spring-boot-starter-actuator依赖提供了很多监控所需的接口,也是必须引入的 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!--<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</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>

    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>2.3.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>

    <!--elk依赖  -->
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
    </dependency>

</dependencies>

`

启动类:

` @EnableFeignClients(basePackages = {"com.mlc.cloud.start"}) @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) @EnableDiscoveryClient public class GatewayApplication {

public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class,args);
}

}

` 关键的实现动态路由逻辑代码

`/**

  • 动态更新路由网关service

  • 1)实现一个Spring提供的事件推送接口ApplicationEventPublisherAware

  • 2)提供动态路由的基础方法,可通过获取bean操作该类的方法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。 */ @Slf4j @Service public 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"; } } `

通过nacos下发动态路由配置,监听Nacos中gateway-route配置

`@Component @Slf4j @DependsOn({"gatewayConfig"}) // 依赖于gatewayConfig bean public 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;
    }
}

} `

配置文件路由

`@Configuration("gatewayConfig") public 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;
}

} `

nacos 配置详情

1618646066(1).png

至此我们实现动态路由就结束了,不足之处,欢迎交流指正