SpringCloud Gateway+Nacos, how to dynamically implement 1000+ routing rules?

Environment: SpringBoot2.7.12 + SpringCloud 2021.0.7 + Spring Cloud Alibaba 2021.0.4.0

1. Introduction

What is Gateway?

Spring Cloud Gateway is part of the Spring Cloud ecosystem and is a gateway server built with technologies such as Spring Framework 5, Spring Boot 2, and Project Reactor. It is mainly used to provide routing, load balancing, security, current limiting, degradation and other functions for microservice applications, and has the advantages of high performance, high throughput and low latency.

The main features of Spring Cloud Gateway include: a reactive programming model based on the asynchronous non-blocking Reactor framework, which has the advantages of high performance, high throughput and low latency. It supports multiple routing strategies, including routing based on path, request parameters, request headers, host, etc. At the same time, Spring Cloud Gateway also supports a variety of filters, including preset global filters and customized local filters, which are used to implement functions such as request forwarding, request modification, request logs, request verification, and request caching.

In addition, Spring Cloud Gateway also supports the integration of Spring Cloud Security, which can implement functions such as identity authentication, authorization, and security restrictions. At the same time, it also supports dynamic routing configuration and dynamic filter configuration, and can dynamically add, modify and delete routes and filters at runtime.

What is Nacos?

Nacos is an open source dynamic service discovery, configuration management and service management platform launched by Alibaba. It is designed to help users discover, configure and manage microservices, and provides an easy-to-use feature set to help users quickly implement dynamic service discovery, service configuration, service metadata and traffic management.

Nacos is committed to building a modern application architecture centered on “services”, such as the microservice paradigm and the cloud native paradigm. As a service infrastructure, Nacos supports a variety of core features, including service discovery, service health monitoring, dynamic configuration of services, dynamic DNS services, and management of services and their metadata.

2. Environment configuration

Dependency Management

<properties>
  <java.version>17</java.version>
  <spring-cloud.version>2021.0.7</spring-cloud.version>
  <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
</properties>
<dependencies>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>
</dependencies>

If you are not based on the service discovery mechanism, then after adding the above starter-gateway dependency, you can configure static routing in the configuration file.

3. Static routing

spring:
  cloud:
    gateway:
      metrics:
        enabled: true
      enabled: true
      discovery:
        locator:
          enabled: false
          lowerCaseServiceId: true
      default-filters:
      -StripPrefix=1
      routes:
      - id: rt-001
        uri: http://www.baidu.com
        predicates:
        - Path=/api-x/**

Browser input: http://localhost:8188/api-x/ can jump directly to baidu.

In most cases, you can configure routing in the application.yml file above in the project, but if there are new changes, the service must be restarted to take effect. So is there any way we can dynamically modify or add routing information? There are two ways to achieve this:

  1. Dynamic management of gateways through actuator

  2. Combined with Nacos dynamic configuration + event mechanism

The first method is actually relatively simple. We only need to introduce the corresponding dependencies and combine it with the Endpoint to achieve it. We will not introduce this method here. If necessary, you can refer to the official documentation.

This article mainly talks about how to clear Nacos to achieve dynamic management of routing.

4. Dynamic routing

First, you need to introduce Nacos-related dependencies.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</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>

Add the bootstrap.yml file to the configuration file

spring:
  application:
    name: cloud-gateway
---
spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      username: nacos
      password: nacos
      discovery:
        enabled: true
        group: cloudApp
      config:
        file-extension:yaml
        group: cloudApp

The registration and configuration management of the service are configured above.

Route definition information:

{
    //Route ID
    "id": "",
    // routing predicate
    "predicates": [],
    //Route filter
    "filters": [],
    //Route uri
    "uri": "",
    //Metadata information
    "metadata": {},
    // order
    "order": 0
}

Next, create a new DataId=shared-routes.json configuration through the nacos console:

The configuration content is a json array, where we can configure routing information arbitrarily.

I have the above configuration information. How can I make the above configuration information take effect in the gateway?

To execute, add the following Bean to the project, which is specifically used to monitor the above configuration content:

@Component
public class DynamicRouteComponent {
  @Resource
  private InMemoryRouteDefinitionRepository routeDefinitionRepository ;
  @Resource
  private ApplicationEventMulticaster multicaster;
  
  @PostConstruct
  public void init() throws Exception {
    ConfigService cs = this.ncm.getConfigService();
    cs.addListener("shared-routes.json", "CAPP", new Listener() {
      @Override
      public void receiveConfigInfo(String configInfo) {
        routeDefinitionRepository.getRouteDefinitions().doOnNext(def -> {
          routeDefinitionRepository.delete(Mono.just(def.getId())).subscribe();
        }).subscribe();
        ObjectMapper mapper = new ObjectMapper();
        TypeReference<List<RouteDefinition>> ref = new TypeReference<>() {};
        try {
          List<RouteDefinition> routeDefinitions = mapper.readValue(configInfo, ref);
          routeDefinitions.forEach(routeDefintion -> {
            routeDefinitionRepository.save(Mono.just(routeDefintion)).subscribe();
          }) ;
          multicaster.multicastEvent(new RefreshRoutesEvent(routeDefinitions));
        }
      }
      @Override
      public Executor getExecutor() {
        return new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
      }
    });
  }
}

In fact, the above publishing event does not need to be done. After our service is started, nacos will perform a scheduled refresh operation of the route every 30 seconds. However, adding event publishing yourself can refresh the route immediately and take effect immediately.

complete! ! !