SpringCloud Gateway (4)动态路由

407 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

   配置网关路由一般有两种方式

        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)的过滤器配置。

       最后贴上源代码

github.com/kid626/spri…