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
网关过滤器对象。
CachingRouteDefinitionLocator
:路由声明缓存器
CompositeRouteDefinitionLocator:
基于组合模式的管理其他的RouteDefinitionLocator实现类
PropertiesRouteDefinitionLocator:
基于配置的路由声明加载器
InMemoryRouteDefinitionRepository:
基于jvm内存的路由声明加载器,可以通过接口对缓存在内存中的路由声明进行新增和删除。
RedisRouteDefinitionLocatorRepository:
基于redis的路由声明加载器,可以通过接口对缓存在redis中的路由声明进行增删。
DiscoveryClientRouteDefinitionRepository:
基于服务发现的路由声明加载器,默认是以spring.application.name
作为Path断言的前缀来实现路由的。
默认情况下:
CachingRouteDefinitionLocator
通过CompositeRouteDefinitionLocator
进行路由声明加载,然后缓存在自己的容器中。
CompositeRouteDefinitionLocator
里面管理着PropertiesRouteDefinitionLocator
通过实现RouteDefinitionRepository
接口,来实现基于mysql路由声明加载器。
/**
* RouteDefinitionLocator 提供了获取路由声明的接口
* RouteDefinitionWriter 提供了路由删除与新增接口
*/
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
}
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仓库地址。
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');
从这里可以看到,sop-boot-consumer是通过配置文件声明的,而sop-boot-provider是通过数据库进行加载的。
这样就实现了基于mysql的自定义路由声明加载器功能了。
路由生效具备时间差
当前路由刷新的功能是通过NacosWatch
的定时任务发布HeartBeatEvent
事件
RouteRefreshListener
监听到HeartBeatEvent,就发布RefreshRoutesEvent
事件
这个时候,RefreshRoutesEvent
就会被CachingRouteLocator
监听到从而实现路由的重新加载功能。
预备解决方案:
基于服务发现的方式去加载路由和删除路由。就是修改service_route_definition表中的in_use字段。