1、准备一个Nacos环境跟一个普普通通的Spring Cloud项目

2、导包,我这里是把Nacos的注册中心跟配置中心都搞了,动态配置的话主要是用到配置中心,主要是这几个包,部分工具类的包或者是spring cloud的其他包就忽略了。

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2021.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.1</version>
        </dependency>

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

        <!--        spring-cloud-starter-gateway:路由转发、请求过滤(权限校验、限流以及监控等)-->
        <!--        spring-boot-starter-webflux:反应式Web框架-->
        <!--        spring-boot-starter-actuator:监控系统健康-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.8</version>
        </dependency>

导包说明:spring-boot-starter-web包跟gateway的包是有冲突的,所以这玩意不能要,有的记得删掉。

另外gateway+spring boot +spring cloud 的版本都是挂钩的,随便搞版本的话,大概率是要交学费的。

我这里spring boot的版本是2.3.12.RELEASE,spring cloud的版本是Hoxton.SR12,gateway的版本是2.2.9.RELEASE,仅供参考,具体可以搜一下,有对照表的。

3、配置,这里不作说明了,具体含义可以查看官网:nacos.io

spring:
  freemarker:
    check-template-location: false
  profiles:
    active: dev
  jackson:
    time-zone: GMT+8
  application:
    name: gateway-router
  datasource:
    druid:
      test-on-borrow=true:
  cloud:
    nacos:
      #这个是注册中心
      discovery:
        server-addr: 192.168.2.200:8848
        service: gateway-router
      #这个才是配置中心
      config:
        server-addr: 192.168.2.200:8848
        name: gateway-router
        namespace: public
        group: DEFAULT_GROUP
    loadbalancer:
      ribbon:
        enabled: false

如果发现Nacos的心跳日志一直打,很烦人的话,可以加上这句屏蔽掉

logging:
  level:
    com.alibaba.nacos.client.config.impl: WARN

4、application启动类记得加上标注

@EnableDiscoveryClient

5、配置读取类GatewayConfig,与上方的配置项对应,不要漏,会报错。

@Configuration
@Slf4j
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.config.server-addr}")
    public void setNacosServerAddr(String nacosServerAddr) {
        NACOS_SERVER_ADDR = nacosServerAddr;
    }

    @Value("${spring.cloud.nacos.config.namespace}")
    public void setNacosNamespace(String nacosNamespace) {
        NACOS_NAMESPACE = nacosNamespace;
    }

    @Value("${spring.cloud.nacos.config.name}")
    public void setNacosRouteDataId(String nacosRouteDataId) {
        NACOS_ROUTE_DATA_ID = nacosRouteDataId;
    }

    @Value("${spring.cloud.nacos.config.group}")
    public void setNacosRouteGroup(String nacosRouteGroup) {
        NACOS_ROUTE_GROUP = nacosRouteGroup;
    }
}

6、动态路由基础控制类DynamicRouteServiceImpl,我抄过来的时候是带log代码的,这里我都去掉了,自己加就好。

@Slf4j
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;

    /**
     * 发布事件
     */
    @Autowired
    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    /**
     * 删除路由
     * @param id
     * @return
     */
    public String delete(String id) {
        try {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "delete success";
        } catch (Exception e) {
            return "delete fail";
        }
    }

    /**
     * 更新路由
     * @param definitions
     * @return
     */
    public String updateList(List<RouteDefinition> definitions) {
        // 删除缓存routerDefinition
        List<RouteDefinition> routeDefinitionsExits =  routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
        if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
            routeDefinitionsExits.forEach(routeDefinition -> {
                delete(routeDefinition.getId());
            });
        }
        definitions.forEach(definition -> {
            updateById(definition);
        });
        return "success";
    }

    /**
     * 更新路由
     * @param definition
     * @return
     */
    public String updateById(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) {
            return "update route fail";
        }
    }

    /**
     * 增加路由
     * @param definition
     * @return
     */
    public String add(RouteDefinition definition) {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }
}

7、核心注册类DynamicRouteServiceImplByNacos

@Component
@Slf4j
@DependsOn({"gatewayConfig"}) // 依赖于gatewayConfig bean
public class DynamicRouteServiceImplByNacos {

    @Autowired
    private DynamicRouteServiceImpl dynamicRouteService;


    private ConfigService configService;

    @PostConstruct
    public void init() {
        try{
            configService = initConfigService();
            if(configService == null){
                return;
            }
            //从nacos拿初始配置
            String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT);
            List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
            for(RouteDefinition definition : definitionList){
                dynamicRouteService.add(definition);
            }
        } catch (Exception e) {
        }
        //注册监听,nacos配置有更新的话同步更新一下配置
        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) {
                    List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class);
                    dynamicRouteService.updateList(definitionList);
                }
                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException 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) {
            return null;
        }
    }

}

8、Nacos配置

Data ID:gateway-router 

//这里是以json的形式,所以直接与服务名称一致即可,如果用的是yml格式,估计得用“${prefix}-${spring.profiles.active}.${file-extension}”格式

Group:DEFAULT_GROUP

配置类型是json,内容:

[{
    "id": "test-router",
    "order": 0,
    "predicates": [{
        "args": {
            "pattern": "/test/**"
        },
        "name": "Path"
    }],
    "filters":[{
        "args": {
            "_genkey_0":"1"
        },
        "name":"StripPrefix"
    }],
    "uri": "lb://test-service"
},{
    "id": "baidu-router",
    "order": 2,
    "predicates": [{
        "args": {
            "pattern": "/baidu/**"
        },
        "name": "Path"
    }],
    "uri": "http://baidu.com"
}]

这里配置了两个路由:

第一个路由是将/test/**规则转发到test-service服务,并且去掉前缀(即:/test/home等同于http://test-service/home)

第二个路由是将/baidu/**规则转发到百度,且不去掉前缀(即:/baidu/home等同于访问http://baidu.com/baidu/home)

9、运行

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。