gateway 集成 nacos 实现动态路由

本文详细介绍了如何在Spring Boot应用中结合Nacos实现动态路由,包括配置步骤、核心类设置以及静态路由与动态路由的区别。动态路由通过路由协议自动更新,适合大规模网络,而静态路由则在安全和效率上有优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

gateway 集成 nacos 实现动态路由,前几篇单独分析gateway网关时,仅介绍了静态路由的原理和使用,今天我们先分析动态路由,然后介绍一下两者的区别和优缺点。

一、动态路由实战分析:

1、构建一个springboot 服务,去掉web相关的jar:

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

加上:

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

 引入相关核心jar

artifactId>common-gateway</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>common-gateway</name>
    <description>网关服务</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!-- springboot web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</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>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>

    </dependencies>

2、项目中yaml配置:

spring:
  main:
    web-application-type: reactive
  application:
    name: @project.artifactId@
    version: @project.version@
  cloud:
    nacos:
      config:
        namespace: common
        file-extension: yaml
        shared-configs:
          - data-id: common-shared-config.yaml
            group: common
            refresh: true
loggingRoot: logs

3、idea中nacos环境配置:

4、nacos页面查找配置:

 5、在 common-gateway-dev.yaml 处点击详情查看:

 配置内容:

server:
  port: 80000
  servlet:
    context-path: /gateway-server
loggingRoot: logs
spring:
  cloud:
    nacos:
      server-addr: 98.10.1.12:80
      username: common
      password: common
      config:
        refresh-enabled: true
      discovery:
        namespace: 7da18ff0-32b8-490c-9ec4-d3ee3d1963b4
        group: DEFAULT_GROUP

  gateway:
    globalcors:
      cors-configurations:
        '[/**]':
          allow-credentials: true
          allowed-origins: "*"
          allowed-methods: "*"
          allowed-headers: "*"
      add-to-simple-url-handler-mapping: true
management:
  endpoints:
    web:
      exposure:
        include: "*"
dynamic:
  route:
    enabled: true
    dataId: gateway-dynamic-dev.json
    group: DEFAULT_GROUP
    namespace: 7da18ff0-32b8-490c-9ec4-d3ee3d1963b4
    serverAddr: 98.10.1.12:80
    username: common
    password: common
filter:
  authorize:
    ignoreuris:
      - /api/good-api/v
      - /api/good-server/v
      - /api/goodorder-api/v
      - /api/goodorder-server/v
route: 
  interfaces: 
    -
      code: auth-server
      interfaceApi: /api/auth-server/test/test1
      serviceName: common-auth-server
      
profile: env
erpx: 
  internal: 
    address: localhost

 里面包含一个json路由配置文件:gateway-dynamic-dev.json,点开详情,内容如下:


[{
    "id": "goods-api",
    "order": 9,
    "predicates": [{
        "args": {
            "pattern": "/api/goods-api/**"
        },
        "name": "Path"
    }],
    "filters":[{
        "args": {
            "_genkey_0": "2"
        },
        "name": "StripPrefix"
    }],
    "uri": "lb://common-goods-xapi"
},
{
    "id": "goods-server",
    "order": 9,
    "predicates": [{
        "args": {
            "pattern": "/api/goods-server/**"
        },
        "name": "Path"
    }],
    "filters":[{
        "args": {
            "_genkey_0": "2"
        },
        "name": "StripPrefix"
    }],
    "uri": "lb://common-goods-server"
},
{
    "id": "goodorder-api",
    "order": 34,
    "predicates": [{
        "args": {
            "pattern": "/api/goodorder-api/**"
        },
        "name": "Path"
    }],
    "filters":[{
        "args": {
            "_genkey_0": "2"
        },
        "name": "StripPrefix"
    }],
    "uri": "lb://common-goodorder-api"
},
{
    "id": "goodorder-server",
    "order": 35,
    "predicates": [{
        "args": {
            "pattern": "/api/goodorder-server/**"
        },
        "name": "Path"
    }],
    "filters":[{
        "args": {
            "_genkey_0": "2"
        },
        "name": "StripPrefix"
    }],
    "uri": "lb://common-goodorder-server"
}]

6、核心类配置:

1)过滤地址配置:

package com.example.nandao.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;


@Configuration
public class ComCorsConfig {

  @Bean
  public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedMethod("*");
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
    source.registerCorsConfiguration("/**", config);

    return new CorsWebFilter(source);
  }
}

2)提取路由参数配置:

package com.example.nandao.config;

import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Description 加载动态路由配置,通过此配置加载配置文件 gateway-dynamic-dev.json 里的路由信息
 **/
@Data
@Component
//https://blog.youkuaiyun.com/weixin_38357164/article/details/101030680
//https://www.jianshu.com/p/9f2c129047c8
//https://www.cnblogs.com/ushowtime/p/15993816.html
@ConfigurationProperties(prefix = "dynamic.route")
//只要存在 ComDynamicRouteConfig 类,便会初始化 ComDynamicRouteProperties 类
@ConditionalOnBean( ComDynamicRouteConfig.class)
public class ComDynamicRouteProperties {

  /**
   * nacos 配置管理  dataId
   */
  private String dataId;
  /**
   * nacos 配置管理 group
   */
  private String group;
  /**
   * nacos 服务地址
   */
  private String serverAddr;

  /**
   * 启动动态路由的标志,默认关闭
   */
  private boolean enabled = false;

  private String namespace;

  private String username;

  private String password;

}

3)动态路由配置:

package com.example.nandao.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.exception.NacosException;
import com.example.nandao.service.impl.ComDynamicRouteServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Description 核心配置
 **/
@Slf4j
@Configuration
@ConditionalOnProperty(name = "dynamic.route.enabled", matchIfMissing = true)
public class ComDynamicRouteConfig {

  private static final List<String> ROUTE_LIST = new ArrayList<>();

  @Autowired//依赖注入,先初始化完成
  private ComDynamicRouteProperties properties;

  @Autowired
  private ComDynamicRouteServiceImpl dynamicRouteService;

  @PostConstruct
  private void init() {
    dynamicRouteByNacosListener();
  }

  private void dynamicRouteByNacosListener() {

    try {
      Properties prop = new Properties();
      prop.setProperty(PropertyKeyConst.NAMESPACE, properties.getNamespace());
      prop.setProperty(PropertyKeyConst.SERVER_ADDR, properties.getServerAddr());
      prop.setProperty(PropertyKeyConst.USERNAME, properties.getUsername());
      prop.setProperty(PropertyKeyConst.PASSWORD, properties.getPassword());

      ConfigService configService = NacosFactory.createConfigService(prop);
      //ConfigService configService = NacosFactory.createConfigService(properties.getServerAddr());
      String content = configService.getConfigAndSignListener(
          properties.getDataId(),
          properties.getGroup(),
          5000,
          new AbstractListener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
              statsConfig(configInfo);
            }
          });
      statsConfig(content);
    } catch (NacosException e) {
      log.error("nacos 获取动态路由配置和监听异常", e);
    }
  }

  private void statsConfig(String configInfo) {

    try {
      clearRoute();
      List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
      for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
        addRoute(routeDefinition);
      }
    } catch (Exception e) {
      log.error("statsConfig异常", e);
    }
  }

  private void clearRoute() {
    for(String id : ROUTE_LIST) {
      dynamicRouteService.delete(id);
    }
    ROUTE_LIST.clear();
  }

  private void addRoute(RouteDefinition definition) {
    dynamicRouteService.add(definition);
    ROUTE_LIST.add(definition.getId());
  }

}

4)动态路由的增删改查:

package com.example.nandao.service.impl;

import lombok.extern.slf4j.Slf4j;
import 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 reactor.core.publisher.Mono;

@Slf4j
@Service
public class ComDynamicRouteServiceImpl implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    /**
     * 增加路由
     * @param definition
     * @return
     */
    public void add(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception ex) {
            log.error(String.format("add -> save route fail【routeId: %s】", definition.getId()), ex);
        }
    }

    /**
     * 更新路由
     * @param definition
     * @return
     */
    public void update(RouteDefinition definition){

        try {
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        } catch (Exception ex) {
            log.error(String.format("update -> update route fail【routeId: %s】", definition.getId()), ex);
        }
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception ex) {
            log.error(String.format("update -> save route fail【routeId: %s】", definition.getId()), ex);
        }
    }

    /**
     * 删除路由
     * @param id
     * @return
     */
    public void delete(String id) {
        try {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception ex) {
            log.error(String.format("delete -> delete route fail【routeId: %s】", id), ex);
        }
    }

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

}

5)boot核心启动类:

package com.example.nandao;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class NandaoApplication {

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

}

到此基本配置大概完成,注释也很清晰。

二、静态路由和动态路由的区别:

1、静态路由特点:

静态路由必须由管理员手工指定。当网络拓扑发生变化时,需要管理员手工更新配置。
   同时静态路由只适合简单小型的网络,当网络结构复杂、路由条目繁多的时候,静态路由将难以胜任。静态路由是管理员通过手动配置的,不便于拓展网络拓扑,一旦网络拓扑发生改变,
  静态路由配置量会很大。动态路由是路由器通过网络协议,动态的学习路由,当网络拓扑发生变化的时候,路由器会根据路由协议自动学习新的路由。

2、动态路由特点:

动态路由通过网络中运行的路由协议收集网络信息。当网络拓扑发生变化时,路由器会自动更新路由信息,不必管理员手工去更新。动态路由,因为OSPF,RIP等路由协议都会周期更新,所以他的更新量会很大,会很占带宽。

3、两者对比:

通过对比后可以发现,动态路由协议更适用于大规模的网络部署。使用静态路由的好处是网络安全保密性高。动态路由因为需要路由器之间频繁地交换各自的路由表,而对路由表的分析可以揭示网络的拓扑结构和网络地址等信息。

4、精炼总结:

静态路由:安全,占用带宽小,简单,高效,转发效率高。
动态路由:灵活性高。

到此,关于动态路由的原理和使用分享完毕,下篇我们分析网关中的过滤器使用,敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值