Spring Cloud Gateway:使用RestController动态更新路由

相关类介绍

动态路由(自己控制,非注册中心控制)涉及两个很重要的Bean:

  • RouteDefinitionWriter:用于添加、修改、删除路由规则。
  • RouteDefinitionLocator:用于查询路由规则。

以及一个相关事件:

  • RefreshRoutesEvent:用于更新路由规则,使上述的操作生效。

依赖及配置

先引入相关依赖:

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chilun</groupId>
    <artifactId>API_OPEN_SPACE_GATEWAY</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>2.7.9</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.5.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
    </dependencies>
</project>

以及配置文件application.yml:

复制代码
server:
  port: 8002
logging:
  level:
    org.springframework.cloud.gateway: DEBUG
myGateway:
  RoutePrefix: api
spring:
  profiles:
    active: dev
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  cloud:
    gateway:
      enabled: true

服务中心相关配置:

复制代码
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=gateway

核心代码

定义服务层接口:

java 复制代码
import com.chilun.apiopenspace.gateway.entity.dto.DeleteRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.InitRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.SaveOrUpdateRouteRequest;
import com.chilun.apiopenspace.gateway.entity.pojo.RoutePOJO;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author 齿轮
 * @date 2024-02-08-22:39
 */
public interface RouteService {
    void init(InitRouteRequest request);
    void saveOrUpdate(SaveOrUpdateRouteRequest request);
    void delete(DeleteRouteRequest request);
    void refresh();
    Mono<List<RoutePOJO>> getAll();
}

实现类(适配器模式):

java 复制代码
package com.chilun.apiopenspace.gateway.service.impl;

import com.chilun.apiopenspace.gateway.entity.dto.DeleteRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.InitRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.SaveOrUpdateRouteRequest;
import com.chilun.apiopenspace.gateway.entity.pojo.RoutePOJO;
import com.chilun.apiopenspace.gateway.service.RouteService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
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 reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 齿轮
 * @date 2024-02-08-22:43
 */
@Service
public class RouteServiceAdepter implements RouteService, ApplicationEventPublisherAware {
    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;
    @Resource
    private RouteDefinitionLocator locator;

    @Value("${myGateway.RoutePrefix}")
    private String routePrefix;

//    @PostConstruct
//    public void test(){
//        SaveOrUpdateRouteRequest saveOrUpdateRouteRequest = new SaveOrUpdateRouteRequest();
//        saveOrUpdateRouteRequest.setUri("https://www.baidu.com");
//        saveOrUpdateRouteRequest.setId("1");
//        saveOrUpdate(saveOrUpdateRouteRequest);
//    }

    @Override
    public void init(InitRouteRequest request) {
        request.getList().forEach(this::saveOrUpdate);
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void saveOrUpdate(SaveOrUpdateRouteRequest request) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(request.getId());
        routeDefinition.setUri(URI.create(request.getUri()));
        String Path = "/" + routePrefix + "/" + request.getId();
        routeDefinition.setPredicates(Collections.singletonList(new PredicateDefinition("Path=" + Path)));
        routeDefinition.setFilters(Collections.singletonList(new FilterDefinition("SetPath=/")));
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
    }

    @Override
    public void delete(DeleteRouteRequest request) {
        routeDefinitionWriter.delete(Mono.just(request.getId())).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public void refresh() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public Mono<List<RoutePOJO>> getAll() {
        return locator.getRouteDefinitions()
                .map(routeDefinition -> {
                    RoutePOJO routePOJO = new RoutePOJO();
                    routePOJO.setId(routeDefinition.getId());
                    routePOJO.setURI(routeDefinition.getUri().toString());
                    routePOJO.setPredicates(routeDefinition.getPredicates().stream()
                            .map(predicate -> predicate.getArgs().values().toString())
                            .collect(Collectors.toList()));
                    routePOJO.setFilters(routeDefinition.getFilters().stream()
                            .map(FilterDefinition::getName)
                            .collect(Collectors.toList()));
                    return routePOJO;
                })
                .collectList();
    }

    private ApplicationEventPublisher publisher;

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

Controller层代码:

java 复制代码
package com.chilun.apiopenspace.gateway.controller;

import com.chilun.apiopenspace.gateway.entity.dto.DeleteRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.InitRouteRequest;
import com.chilun.apiopenspace.gateway.entity.dto.SaveOrUpdateRouteRequest;
import com.chilun.apiopenspace.gateway.entity.pojo.RoutePOJO;
import com.chilun.apiopenspace.gateway.service.RouteService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author 齿轮
 * @date 2024-02-13-22:13
 */
@RestController
@RequestMapping("/manage")
public class RouteController {
    @Resource
    RouteService service;

    @PostMapping("/update")
    public void add(@RequestBody SaveOrUpdateRouteRequest request) {
        service.saveOrUpdate(request);
    }

    @PostMapping("/init")
    public void init(@RequestBody InitRouteRequest request) {
        service.init(request);
    }

    @PostMapping("/delete")
    public void delete(@RequestBody DeleteRouteRequest request) {
        service.delete(request);
    }

    @GetMapping("/getAll")
    public Mono<List<RoutePOJO>> getAll() {
        return service.getAll();
    }

    @GetMapping("/refresh")
    public void refresh(){
        service.refresh();
    }
}

代码请自行查看,看不懂就问大语言模型。

代码逻辑较简单,仅基本增删改查功能,其他延伸方面如安全等请自行探索。

其他代码

实体类:

java 复制代码
package com.chilun.apiopenspace.gateway.entity.dto;

import lombok.Data;

/**
 * @author 齿轮
 * @date 2024-02-13-20:01
 */
@Data
public class DeleteRouteRequest {
    String id;
}

package com.chilun.apiopenspace.gateway.entity.dto;

import lombok.Data;

import java.util.List;

/**
 * @author 齿轮
 * @date 2024-02-13-20:00
 */
@Data
public class InitRouteRequest {
    List<SaveOrUpdateRouteRequest> list;
}

package com.chilun.apiopenspace.gateway.entity.dto;

import lombok.Data;

/**
 * @author 齿轮
 * @date 2024-02-13-19:55
 */
@Data
public class SaveOrUpdateRouteRequest {
    String id;
    String uri;
}

package com.chilun.apiopenspace.gateway.entity.pojo;

import lombok.Data;

import java.util.List;

/**
 *
 * Route实体类,用于远程调用接受参数
 * @author 齿轮
 * @date 2024-02-13-19:53
 */
@Data
public class RoutePOJO {
    String id;
    String URI;
    List<String> Predicates;
    List<String> Filters;
}

swagger文档配置类:

java 复制代码
package com.chilun.apiopenspace.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author 齿轮
 * @date 2024-02-14-1:32
 */
@Configuration
@EnableSwagger2
@Profile({"dev"})
public class Knife4jConfig {
    @Bean
    public Docket defaultApi2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .title("API开放平台接口文件")
                        .description("API开放平台")
                        .contact(new Contact("chilun", "http://home.shoumingchilun.cn", "2265501251@qq.com"))
                        .version("1.0")
                        .build())
                .select()
                // 指定 Controller 扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.chilun.apiopenspace.gateway.controller"))
                .paths(PathSelectors.any())
                .build();
    }
}
相关推荐
Rain的Java大神之路1 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
需要8261 天前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
需要8262 天前
Arthas 一个命令排查线上问题
java·jvm·spring·spring cloud
Raas1002 天前
MAI Gateway(魔芋企业级AI网关)功能全解:AI网关支持流式输出吗?一文看懂AI网关能力矩阵
大数据·人工智能·gateway·mai gateway·企业级网关
苏渡苇3 天前
Spring Insight 里如何把 Span 画成瀑布时间线
后端·spring·spring cloud·springboot·监控·apm
叶总没有会3 天前
Nacos—注册中心
spring cloud·微服务
毅炼3 天前
不写多语言 SDK,怎么让 Python、Go、Node 服务接入注册中心?
java·后端·系统架构·gateway
苏渡苇4 天前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring·spring cloud·系统监控·apm
叶总没有会4 天前
微服务--分布式基础
java·spring·spring cloud·微服务
孙启超4 天前
【AI开发之Rust】第 7 课:错误处理 —— panic、Result 与 `?`
人工智能·分布式·后端·爬虫·spring cloud·架构·rust