springcloud gateway通过数据库获取路由信息

在 Spring Cloud Gateway 中结合 MyBatis 动态从数据库加载路由配置,可以实现灵活的路由管理。以下是详细实现步骤:


1. 数据库表设计

创建路由配置表 gateway_route

CREATE TABLE `gateway_route` (
  `id` varchar(50) NOT NULL COMMENT '路由唯一标识',
  `uri` varchar(200) NOT NULL COMMENT '目标服务地址(如 lb://user-service)',
  `predicates` text COMMENT '路由断言(JSON 数组格式)',
  `filters` text COMMENT '路由过滤器(JSON 数组格式)',
  `order` int(11) DEFAULT '0' COMMENT '路由优先级',
  `enabled` tinyint(1) DEFAULT '1' COMMENT '是否启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码
路由配置字段说明
字段名 含义
id user_route 路由的唯一标识符,用于区分不同路由规则
uri http://127.0.0.1:8081 目标服务地址,请求将被转发到此地址
predicates [{"name":"Path","args":{"pattern":"/test/**"}}] 路由断言规则,匹配请求路径
filters [{"name":"StripPrefix","args":{"parts":"1"}}] 路由过滤器,修改请求路径
order 0 路由优先级(数值越小优先级越高)
enabled 1 是否启用该路由(1-启用,0-禁用)

示例数据:

INSERT INTO `gateway_route` 
VALUES ('user_route', 'lb://user-service', 
        '[{"name":"Path","args":{"pattern":"/api/users/**"}}]',
        '[{"name":"StripPrefix","args":{"parts":"1"}}]',
        0, 1);
复制代码

2. 添加依赖

pom.xml 中添加以下依赖:

<!-- Spring Cloud Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- MyBatis 整合 Spring Boot -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- JSON 处理工具 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

3. 实体类与 Mapper

实体类 GatewayRoute
public class GatewayRoute {
    private String id;
    private String uri;
    private String predicates;  // JSON 字符串,如 [{"name":"Path", "args":{"pattern":"/api/**"}}]
    private String filters;     // JSON 字符串,如 [{"name":"StripPrefix", "args":{"parts":"1"}}]
    private int order;
    private boolean enabled;
    // Getters & Setters
}
复制代码
Mapper 接口
@Mapper
public interface RouteMapper {
    @Select("SELECT * FROM gateway_route WHERE enabled = 1")
    List<GatewayRoute> findAllEnabledRoutes();
}

4. 动态路由加载实现

自定义路由仓库
@Component
public class DbRouteDefinitionRepository implements RouteDefinitionLocator {
    private final RouteMapper routeMapper;
    private final ObjectMapper objectMapper;

    public DbRouteDefinitionRepository(RouteMapper routeMapper, ObjectMapper objectMapper) {
        this.routeMapper = routeMapper;
        this.objectMapper = objectMapper;
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<GatewayRoute> routes = routeMapper.findAllEnabledRoutes();
        return Flux.fromIterable(routes)
                .map(this::convertToRouteDefinition);
    }

    private RouteDefinition convertToRouteDefinition(GatewayRoute route) {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(route.getId());
        definition.setUri(URI.create(route.getUri()));
        definition.setOrder(route.getOrder());

        try {
            // 解析 Predicates
            List<PredicateDefinition> predicates = objectMapper.readValue(
                    route.getPredicates(),
                    new TypeReference<List<PredicateDefinition>>() {}
            );
            definition.setPredicates(predicates);

            // 解析 Filters
            List<FilterDefinition> filters = objectMapper.readValue(
                    route.getFilters(),
                    new TypeReference<List<FilterDefinition>>() {}
            );
            definition.setFilters(filters);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("路由配置解析失败: " + route.getId(), e);
        }

        return definition;
    }
}

5. 配置 MyBatis 和数据源

application.yml 配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/gateway_db?useSSL=false&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis 配置
mybatis:
  type-aliases-package: com.example.gateway.entity  # 实体类包路径
  configuration:
    map-underscore-to-camel-case: true  # 自动驼峰命名转换
启动类添加 Mapper 扫描
@SpringBootApplication
@MapperScan("com.example.gateway.mapper")  // 指定 Mapper 接口所在包
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

6. 动态路由刷新接口

添加一个触发路由刷新的接口:

@RestController
@RequestMapping("/gateway")
public class RouteRefreshController {
    private final ApplicationEventPublisher eventPublisher;

    public RouteRefreshController(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @PostMapping("/refresh")
    public String refreshRoutes() {
        eventPublisher.publishEvent(new RefreshRoutesEvent(this));
        return "路由刷新成功";
    }
}

7. 测试验证

  1. 启动应用:确保数据库连接正常且表数据存在。

  2. 触发路由刷新

    curl -X POST http://localhost:8080/gateway/refresh
    
  3. 验证路由转发

    curl http://localhost:8080/api/users/1
    

    请求应被转发到 user-service 服务。

    INSERT INTO gateway.gateway_route (id,uri,predicates,filters,order,enabled) VALUES
    ('user_route','http://127.0.0.1:8081','[{"name":"Path","args":{"pattern":"/test/**"}}]','[{"name":"StripPrefix","args":{"parts":"1"}}]',0,1);

我启动的是普通的springboot项目,端口是8081

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping(value = "/echo/{string}")
    public String echo(@PathVariable String string) {
        return string;
    }
}

8. 动态更新流程

  1. 修改数据库路由数据:通过 SQL 或管理界面更新路由配置。

  2. 调用刷新接口

    curl -X POST http://localhost:8080/gateway/refresh
    
  3. 观察日志:检查新路由是否加载成功。


常见问题与解决

问题 1:MyBatis Mapper 未注入
  • 错误信息No qualifying bean of type 'com.example.gateway.mapper.RouteMapper'

  • 解决

    1. 确认 @MapperScan 注解指定了正确的包路径。

    2. 检查 Mapper 接口是否有 @Mapper 注解。

问题 2:JSON 解析失败
  • 错误信息路由配置解析失败

  • 解决

    1. 检查数据库中的 predicatesfilters 字段是否符合 JSON 格式。

    2. 使用 JSON 校验工具验证字段内容。

问题 3:路由未生效
  • 排查步骤

    1. 确认数据库中的 enabled 字段为 1

    2. 检查 uri 格式是否正确(如 lb://service-name 需确保服务发现已启用)。


9. 扩展优化

添加缓存

减少数据库频繁查询:

@Component
public class CachedRouteDefinitionRepository implements RouteDefinitionLocator {
    private final RouteDefinitionLocator delegate;
    private final Cache<String, RouteDefinition> cache = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();

    public CachedRouteDefinitionRepository(RouteDefinitionLocator delegate) {
        this.delegate = delegate;
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        return delegate.getRouteDefinitions()
                .doOnNext(route -> cache.put(route.getId(), route));
    }

    public RouteDefinition getRoute(String id) {
        return cache.getIfPresent(id);
    }
}
集成服务发现

uri 中使用 lb:// 格式时,确保已启用服务发现:

spring:
  cloud:
    discovery:
      enabled: true

通过以上步骤,即可实现基于 MyBatis 的 Spring Cloud Gateway 动态路由管理,支持通过数据库灵活配置和实时更新路由规则。

相关推荐
LCY1331 分钟前
django自动添加接口文档
数据库·django·sqlite
黄同学real17 分钟前
解决Windows版Redis无法远程连接的问题
数据库·windows·redis
罗念笙31 分钟前
MySQL InnoDB引擎中的聚簇索引和非聚簇索引有什么区别?
数据库
阿杰来学编程1 小时前
数据库约束
数据库·mysql
JeffreyGu.1 小时前
Oracle中In和Exists区别分析
数据库·oracle
NineData2 小时前
NineData社区版抢先体验,获取无人机、双肩包、充电宝等周边福利
数据库
用户6279947182622 小时前
南大通用GBase 8c B兼容模式的加解密函数实践指南
数据库
hj10433 小时前
记录一个SQL自动执行的html页面
数据库·sql
用户6279947182623 小时前
南大通用GBase 8s数据库包解析
数据库
TiDB_PingCAP3 小时前
TiDB 观测性解读(一)丨索引观测:快速识别无用索引与低效索
数据库·tidb·索引优化