本文已参与「新人创作礼」活动,一起开启掘金创作之路。
配置网关路由一般有两种方式
application.yml 中配置
routes:
#路径匹配
- id: demo-server
uri: lb://demo-server
predicates:
# 这样写方便后面理解
- name: Path
args:
pattern: /api/hi/**
filters:
- name: RewritePath
args:
regexp: /api/(?<segment>.*)
replacement: /${segment}
- AddRequestHeader=X-Request-Foo, Bar
代码里配置
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
无论是yml还是代码,这些配置最终都是被封装到 RouteDefinition 对象中。
我们找到关键类 RouteDefinitionRepository,默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。
@Component
public class DynamicRouteDefinitionRepository implements RouteDefinitionRepository, ApplicationEventPublisherAware {
@Autowired
private GatewayRouteService gatewayRouteService;
private ApplicationEventPublisher eventPublisher;
private Map<String, RouteDefinition> cache = new ConcurrentHashMap<>();
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(cache.values());
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(
routeDefinition -> {
gatewayRouteService.save(RouteDefinitionConverter.convertToDTO(routeDefinition));
this.addCache(routeDefinition);
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(
id -> {
gatewayRouteService.remove(id);
this.removeCache(id);
return Mono.empty();
});
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
/**
* 将指定路由加入到缓存中。
*/
public void addCache(RouteDefinition route) {
this.cache.putIfAbsent(route.getId(), route);
this.publishEvent();
}
/**
* 将指定路由从缓存中删除。
*/
public void removeCache(String routeId) {
if (this.cache.remove(routeId) != null) {
this.publishEvent();
}
}
public void publishEvent() {
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
}
public Map<String, RouteDefinition> getCache() {
return cache;
}
public void setCache(Map<String, RouteDefinition> cache) {
this.cache = cache;
}
public void clearCache() {
this.cache.clear();
}
}
解释一下这边实现的几个关键方法
-
getRouteDefinitions
- 用于返回自定义的路由配置信息,可以从数据库或者缓存中取,自行实现
-
save
- 用于通过 RESTFul API 新增路由信息
- 需要开启 actuator 的 gateway 端点
-
delete
- 用于通过 RESTFul API 删除路由信息
- 需要开启 actuator 的 gateway 端点
我这边采用 mysql 来存储配置路由信息,表结构如下
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for gateway_route
-- ----------------------------
DROP TABLE IF EXISTS `gateway_route`;
CREATE TABLE `gateway_route` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
`route_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '路由id',
`route_order` int(0) NULL DEFAULT 0 COMMENT '路由顺序',
`uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '路由路径',
`is_enable` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Y' COMMENT '是否启用',
`create_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建用户',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新用户',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`is_delete` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'N' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '网关-路由' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of gateway_route
-- ----------------------------
INSERT INTO `gateway_route` VALUES (1, 'demo-server', 0, 'lb://demo-server', 'Y', 'admin', '2022-01-26 17:23:42', NULL, NULL, 'N');
-- ----------------------------
-- Table structure for gateway_route_param
-- ----------------------------
DROP TABLE IF EXISTS `gateway_route_param`;
CREATE TABLE `gateway_route_param` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
`route_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '路由id',
`param_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '参数名称',
`param_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '参数key',
`param_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '参数value',
`type` int(0) NOT NULL COMMENT '1 为predicate,2为 filter',
`is_enable` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Y' COMMENT '是否生效',
`create_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建用户',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新用户',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`is_delete` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'N' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '网关-路由参数表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of gateway_route_param
-- ----------------------------
INSERT INTO `gateway_route_param` VALUES (1, 'demo-server', 'Path', 'pattern', '/api/hi/**', 1, 'Y', 'admin', '2022-01-26 17:26:07', 'admin', '2022-01-26 17:26:07', 'N');
INSERT INTO `gateway_route_param` VALUES (2, 'demo-server', 'RewritePath', 'regexp', '/api/(?<segment>.*)', 2, 'Y', 'admin', '2022-01-26 17:26:32', 'admin', '2022-01-26 17:26:32', 'N');
INSERT INTO `gateway_route_param` VALUES (3, 'demo-server', 'RewritePath', 'replacement', '/$\{segment}', 2, 'Y', 'admin', '2022-02-07 11:10:53', 'admin', '2022-02-07 11:10:53', 'N');
其中 param 中的 key,value 比较不太好理解,官方文档里也暂没有找到比较完整的示例demo。我这边通过网上资料的简单拼凑,实现了路径重写(RewritePath)的过滤器配置。
最后贴上源代码