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 动态路由管理,支持通过数据库灵活配置和实时更新路由规则。

相关推荐
晋阳十二夜5 小时前
【压力测试之_Jmeter链接Oracle数据库链接】
数据库·oracle·压力测试
GDAL6 小时前
Node.js v22.5+ 官方 SQLite 模块全解析:从入门到实战
数据库·sqlite·node.js
DCTANT7 小时前
【原创】国产化适配-全量迁移MySQL数据到OpenGauss数据库
java·数据库·spring boot·mysql·opengauss
AI、少年郎9 小时前
Oracle 进阶语法实战:从多维分析到数据清洗的深度应用(第四课)
数据库·oracle
赤橙红的黄9 小时前
自定义线程池-实现任务0丢失的处理策略
数据库·spring
DataGear10 小时前
如何在DataGear 5.4.1 中快速制作SQL服务端分页的数据表格看板
javascript·数据库·sql·信息可视化·数据分析·echarts·数据可视化
weixin_4383354010 小时前
分布式锁实现方式:基于Redis的分布式锁实现(Spring Boot + Redis)
数据库·redis·分布式
码不停蹄的玄黓10 小时前
MySQL Undo Log 深度解析:事务回滚与MVCC的核心功臣
数据库·mysql·undo log·回滚日志
Qdgr_10 小时前
价值实证:数字化转型标杆案例深度解析
大数据·数据库·人工智能
数据狐(DataFox)10 小时前
SQL参数化查询:防注入与计划缓存的双重优势
数据库·sql·缓存