基于RouteDefinitionRepository实现基于mysql的路由声明加载

  • Post author:
  • Post category:mysql




1.背景

RouteDefinition和Route这两个是springcloud gateway中的路由关键实体,RouteDefinition是给外部使用的,我们可以通过实现

RouteDefinitionLocator

接口,来实现对路由声明获取方式的自定义。


RouteDefinitionLocator

是从外部,比如从属性文件,jvm内存,redis,discovery client加载路由声明到springcloud gateway中。


RouteLocator

是从RouteDefinitionLocator中获取路由声明,然后转化为springcloud gateway可以使用的路由

Route


RouteDefinition



Route


其实主要是断言匹配器的映射和过滤器的映射:

将RouteDefinition中PredicateDefinition中的name通过

RoutePredicateFactory

生成对应的

GatewayPredicate

路由匹配断言对象。

将RouteDefinition中FilterDefinition中的name通过

GatewayFilterFactory

生成对应的

GatewayFilter

网关过滤器对象。



2. RouteDefinitionLocator的子类介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A7iqJWu0-1678797506974)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ada9bafb-6e11-4c6c-b249-5569d43c9870/Untitled.png)]



CachingRouteDefinitionLocator


:路由声明缓存器



CompositeRouteDefinitionLocator:


基于组合模式的管理其他的RouteDefinitionLocator实现类


PropertiesRouteDefinitionLocator:

基于配置的路由声明加载器


InMemoryRouteDefinitionRepository:

基于jvm内存的路由声明加载器,可以通过接口对缓存在内存中的路由声明进行新增和删除。


RedisRouteDefinitionLocatorRepository:

基于redis的路由声明加载器,可以通过接口对缓存在redis中的路由声明进行增删。


DiscoveryClientRouteDefinitionRepository:

基于服务发现的路由声明加载器,默认是以

spring.application.name

作为Path断言的前缀来实现路由的。


默认情况下:


CachingRouteDefinitionLocator

通过

CompositeRouteDefinitionLocator

进行路由声明加载,然后缓存在自己的容器中。


CompositeRouteDefinitionLocator

里面管理着

PropertiesRouteDefinitionLocator



3.自定义mysql路由声明加载器MySqlRouteDefinitionLocatorRepository

通过实现

RouteDefinitionRepository

接口,来实现基于mysql路由声明加载器。

/**
* RouteDefinitionLocator 提供了获取路由声明的接口
* RouteDefinitionWriter 提供了路由删除与新增接口
*/
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
}



3.1 自定义实现

package com.gu.gateway.frame.route;

import com.gu.gateway.service.IServiceRouteDefinitionLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 从数据库中加载路由声明
 */
@Component
public class MySqlRouteDefinitionLocatorRepository implements RouteDefinitionRepository {

    @Autowired
    IServiceRouteDefinitionLocator serviceRouteDefinitionLocator;
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        // 拉取数据库配置进行路由读取
        List<RouteDefinition> routeDefinitionList = serviceRouteDefinitionLocator.getRouteDefinitionList();
        // 将List的RouteDefinition转化为Flux的异步序列
        return Flux.fromIterable(routeDefinitionList);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        // todo 进行路由写入到mysql数据库
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        // todo 从mysql中删除路由
        return null;
    }
}

IServiceRouteDefinitionLocator主要功能将数据库中的路由声明信息转为List

其中主要代码为:


ServiceRouteDefinition

转为

RouteDefinition


ServiceRoutePredicate

转为

PredicateDefinition


ServiceRouteFilter

转为

FilterDefinition


ServiceRouteMetadata

转为

RouteDefinition

的metadata

代码太多,就不贴了,稍后贴出gitee仓库地址。



4. mysql的路由声明表结构

CREATE TABLE `service_route_predicate` (
  `id` int NOT NULL AUTO_INCREMENT,
  `route_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `predicate_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `predicate_arg_name` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `predicate_arg_value` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `in_use` int NOT NULL DEFAULT '0',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `edit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `service_route_metadata` (
  `id` int NOT NULL AUTO_INCREMENT,
  `route_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `meta_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `meta_value` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `in_use` int NOT NULL DEFAULT '0',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `edit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `service_route_filter` (
  `id` int NOT NULL AUTO_INCREMENT,
  `route_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `filter_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `filter_arg_name` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `filter_arg_value` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `in_use` int NOT NULL DEFAULT '0',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `edit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `service_route_definition` (
  `id` int NOT NULL AUTO_INCREMENT,
  `route_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `route_uri` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `route_order` int NOT NULL DEFAULT '0',
  `in_use` int NOT NULL DEFAULT '0',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `edit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
sql:
INSERT INTO `service_route_definition` (`id`, `route_id`, `route_uri`, `route_order`, `in_use`, `create_time`, `edit_time`) 
VALUES (1, 'sop-boot-provider', 'lb://sop-boot-provider', 1, 1, '2023-03-13 11:40:54', '2023-03-14 05:18:08');
INSERT INTO `service_route_filter` (`id`, `route_id`, `filter_name`, `filter_arg_name`, `filter_arg_value`, `in_use`, `create_time`, `edit_time`) 
VALUES (1, 'sop-boot-provider', 'StripPrefix', '_genkey_0', '1', 1, '2023-03-13 11:49:26', '2023-03-14 05:15:08');
INSERT INTO `service_route_predicate` (`id`, `route_id`, `predicate_name`, `predicate_arg_name`, `predicate_arg_value`, `in_use`, `create_time`, `edit_time`) 
VALUES (1, 'sop-boot-provider', 'Path', '_genkey_0', '/provider/**', 1, '2023-03-13 11:45:57', '2023-03-14 05:14:59');



5. 结果

在这里插入图片描述

在这里插入图片描述

从这里可以看到,sop-boot-consumer是通过配置文件声明的,而sop-boot-provider是通过数据库进行加载的。

这样就实现了基于mysql的自定义路由声明加载器功能了。



6.存在问题


路由生效具备时间差

当前路由刷新的功能是通过

NacosWatch

的定时任务发布

HeartBeatEvent

事件


RouteRefreshListener

监听到HeartBeatEvent,就发布

RefreshRoutesEvent

事件

这个时候,

RefreshRoutesEvent

就会被

CachingRouteLocator

监听到从而实现路由的重新加载功能。


预备解决方案:

基于服务发现的方式去加载路由和删除路由。就是修改service_route_definition表中的in_use字段。



版权声明:本文为yang131peng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。