SpringCloudGateway路由定义存至Mysql数据库


SpringCloudGateway默认根据配置文件或代码在启动时路由定义存在内存中,如果要修改路由配置,需要改动配置文件或代码后重启网关,十分不灵活。

官方提供了RedisRouteDefinitionRepository可以将配置存放之Redis中,使用spring.cloud.gateway.redis-route-definition-repository.enabled开启。

可以实现RouteDefinitionRepository接口,将路由配置保存至Mysql中。

使用的SpringCloud版本为2021.0.5

ORM使用SpringDataR2DBC,相关教程请自行查找。

项目POM引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
    <groupId>com.github.jasync-sql</groupId>
    <artifactId>jasync-r2dbc-mysql</artifactId>
</dependency>

路由定义实体

@Table(name = "`gateway_route_define`")
public class GatewayRouteDefineEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private String id;

    private String uri;

    private String predicates;

    private String filters;

    private String metadata;

    @Column("`is_enable`")
    private Boolean enable;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public String getPredicates() {
        return predicates;
    }

    public void setPredicates(String predicates) {
        this.predicates = predicates;
    }

    public String getFilters() {
        return filters;
    }

    public void setFilters(String filters) {
        this.filters = filters;
    }

    public String getMetadata() {
        return metadata;
    }

    public void setMetadata(String metadata) {
        this.metadata = metadata;
    }

    public Boolean isEnable() {
        return enable;
    }

    public void setEnable(Boolean enable) {
        this.enable = enable;
    }
}

DAO层

@Repository
public interface GatewayRouteDefineDao extends ReactiveCrudRepository<GatewayRouteDefineEntity, String> {

    /**
     * 查询所有启用的路由定义
     * @return 路由定义列表
     */
    Flux<GatewayRouteDefineEntity> findAllByEnableTrue();
}

Service层

public interface GatewayRouteDefineService {

    /**
     * 查询所有实体
     * @return 路由实体列表
     */
    Flux<GatewayRouteDefineEntity> findAll();

    /**
     * 保存
     * @param entity 路由实体
     * @return
     */
    Mono<GatewayRouteDefineEntity> save(@RequestBody @Validated GatewayRouteDefineEntity entity);

    /**
     * 根据id删除实体
     * @param id 实体id
     * @return
     */
    Mono<Void> deleteById(@PathVariable("id") String id);

    /**
     * 刷新路由定义
     * @return
     */
    Mono<Void> refreshRouteDefinition();
}

@Service
public class GatewayRouteDefineServiceImpl implements GatewayRouteDefineService {

    private static final Logger log = LoggerFactory.getLogger(GatewayRouteDefineServiceImpl.class);

    private final GatewayRouteDefineDao gatewayRouteDefineDao;

    private final ApplicationEventPublisher publisher;


    public GatewayRouteDefineServiceImpl(GatewayRouteDefineDao gatewayRouteDefineDao,
                                         ApplicationEventPublisher publisher) {
        this.gatewayRouteDefineDao = gatewayRouteDefineDao;
        this.publisher = publisher;
    }

    @Override
    public Flux<GatewayRouteDefineEntity> findAll() {
        return gatewayRouteDefineDao.findAllByEnableTrue();
    }

    @Override
    public Mono<GatewayRouteDefineEntity> save(GatewayRouteDefineEntity entity) {
        return gatewayRouteDefineDao.save(entity);
    }

    @Override
    public Mono<Void> deleteById(String id) {
        return gatewayRouteDefineDao.deleteById(id);
    }    @Override
    public Mono<Void> refreshRouteDefinition() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return Mono.empty();
    }
}

实现RouteDefinitionRepository

@Configuration
public class RouteDefinitionConfiguration {

    @Bean
    public DbRouteDefinitionRepository dbRouteDefinitionRepository(
            GatewayRouteDefineService gatewayRouteDefineService) {
        return new DbRouteDefinitionRepository(gatewayRouteDefineService);
    }

    public static class DbRouteDefinitionRepository implements RouteDefinitionRepository {

        private final GatewayRouteDefineService gatewayRouteDefineService;

        public DbRouteDefinitionRepository(GatewayRouteDefineService gatewayRouteDefineService) {
            this.gatewayRouteDefineService = gatewayRouteDefineService;
        }

        @Override
        public Flux<RouteDefinition> getRouteDefinitions() {
            return gatewayRouteDefineService.findAll()
                    .map(entity -> {
                        try {
                            return GatewayRouteDefineUtils.convert(entity);
                        } catch (URISyntaxException | JsonProcessingException e) {
                            throw new RuntimeException(e);
                        }
                    });
        }

        @Override
        public Mono<Void> save(Mono<RouteDefinition> route) {
            return route.flatMap(r -> {
                try {
                    GatewayRouteDefineEntity entity = GatewayRouteDefineUtils.convert(r);
                    return gatewayRouteDefineService.save(entity).then();
                } catch (JsonProcessingException e) {
                    return Mono.error(new RuntimeException(e));
                }
            });
        }        @Override
        public Mono<Void> delete(Mono<String> routeId) {
            return routeId.flatMap(gatewayRouteDefineService::deleteById);
        }
    }
}

工具类

public class GatewayRouteDefineUtils {

    private static final ObjectMapper OBJECT_MAPPER;

    static {
        OBJECT_MAPPER = new ObjectMapper();
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static RouteDefinition convert(GatewayRouteDefineEntity entity) throws URISyntaxException, JsonProcessingException {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(entity.getId());
        definition.setUri(new URI(entity.getUri()));

        if (entity.getPredicates() != null) {
            List<PredicateDefinition> predicateDefinitions = OBJECT_MAPPER.readValue(
                    entity.getPredicates(),
                    new TypeReference<List<PredicateDefinition>>() { });
            definition.setPredicates(predicateDefinitions);
        }

        if (entity.getFilters() != null) {
            List<FilterDefinition> filterDefinitions = OBJECT_MAPPER.readValue(
                    entity.getFilters(),
                    new TypeReference<List<FilterDefinition>>() { });
            definition.setFilters(filterDefinitions);
        }
        return definition;
    }    public static GatewayRouteDefineEntity convert(RouteDefinition definition) throws JsonProcessingException {
        GatewayRouteDefineEntity entity = new GatewayRouteDefineEntity();
        entity.setId(definition.getId());
        entity.setUri(definition.getUri().toString());
        entity.setPredicates(OBJECT_MAPPER.writeValueAsString(definition.getPredicates()));
        entity.setFilters(OBJECT_MAPPER.writeValueAsString(definition.getFilters()));
        return entity;
    }
}

测试Controller

@RestController
public class TestController {

    private static final Logger log = LoggerFactory.getLogger(TestController.class);

    private final GatewayRouteDefineService gatewayRouteDefineService;

    public TestController(GatewayRouteDefineService gatewayRouteDefineService) {
        this.gatewayRouteDefineService = gatewayRouteDefineService;
    }

    @RequestMapping("/refresh_route")
    public Mono<String> refreshRoute() {
        return gatewayRouteDefineService.refreshRouteDefinition()
                .thenReturn("refresh success");
    }
}

数据库表

CREATE TABLE `gateway_route_define`  (
  `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  `uri` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '目的地址',
  `predicates` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '断言定义',
  `filters` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '过滤器定义',
  `metadata` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '扩展数据',
  `is_enable` tinyint(4) NULL DEFAULT NULL COMMENT '是否启用  0:否  1:是',
  `create_time` datetime(3) NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '网关路由定义表' ROW_FORMAT = Dynamic;



插入一条测试数据

INSERT INTO `gateway_route_define` VALUES ('c707a482-77a3-11ed-a1eb-0242ac120002', 'https://www.baidu.com', '[{\"name\":\"Path\",\"args\":{\"pattern\":\"/baidu\"}}]', '[{\"name\":\"RewritePath\",\"args\":{\"regexp\":\"/baidu/?(?<segment>.*)\",\"replacement\":\"/$\\\\{segment}\"}},{\"name\":\"GuavaRateLimiter\",\"args\":{\"limit\":\"5\"}}]', '{}', 1, '2022-12-12 11:46:55.000');



其实数据库保存的字段就是RouteDefinition类中的字段,其中predicates、filters、metadata字段为JSON格式的数据。

路由定义序列化
Predicate序列化
其实就是对PredicateDefinition类进行序列化,包含name和args两个字段,其中args需要去org.springframework.cloud.gateway.handler.predicate路径下找具体的工厂类,里面有各参数的名称
例如

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

这条路由规则中的

Path=/red/{segment},/blue/{segment}

其实现类是PathRoutePredicateFactory,那么args参数就是由PathRoutePredicateFactory.Config序列化而来。其余predicate和filter都是按此规则,这样应该就能理解插入的测试数据中predicates和filters字段的含义了。

测试
启动网关,会自动去数据库加载路由定义,当数据库路由修改后,可以调用GatewayRouteDefineService#refreshRouteDefinition来刷新路由,其实原理就是触发一个RefreshRoutesEvent事件,SpringCloudGateway会重新加载路由。
————————————————
原文链接:https://blog.csdn.net/Hugh_W/article/details/128295105

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值