源码梳理:
RouteDefinitionLocator 是路由定义定位器的*接口,它的主要作用就是读取路由的配置信息(org.springframework.cloud.gateway.route.RouteDefinition)。它有五种不同的实现类,如图:
RouteDefinitionLocator
类 : org.springframework.cloud.gateway.route.RouteDefinitionLocator;路由定义定位器接口,只有一个方法,用来获取路由定义列表的方法。
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions(); }
通过 RouteDefinitionLocator 的类图,可以看出该接口有多个实现类:
1、PropertiesRouteDefinitionLocator:基于属性配置
2、DiscoveryClientRouteDefinitionLocator:基于服务发现
3、CompositeRouteDefinitionLocator:组合方式
4、CachingRouteDefinitionLocator:缓存方式
5、其中还有一个接口 RouteDefinitionRepository 继承自RouteDefinitionLocator,用于对路由定义的操作(保存、删除路由定义)
RouteDefinition
RouteDefinition 作为GatewayProperties中的属性,在网关启动的时候读取配置文件中的相关配置信息,源码 其属性如下:
@Validated public class RouteDefinition { /** * 路由唯一ID ,默认为 UUID */ private String id; /** * 路由断言定义列表 */ @NotEmpty @Valid private List<PredicateDefinition> predicates = new ArrayList(); /** * 过滤器定义列表 */ @Valid private List<FilterDefinition> filters = new ArrayList(); /** * 转发地址 */ @NotNull private URI uri; /** * */ private Map<String, Object> metadata = new HashMap(); /** * 优先级 */ private int order = 0; public RouteDefinition() { } public RouteDefinition(String text) { int eqIdx = text.indexOf("="); if (eqIdx <= 0) { throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" + ", must be of the form name=value"); } setId(text.substring(0, eqIdx)); String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); setUri(URI.create(args[0])); for (int i = 1; i < args.length; i++) { this.predicates.add(new PredicateDefinition(args[i])); } } }
进入断言和路由器属性可以看到他们 是一个 Map 数据结构,可以存放多个对应的 键值对数组
RouteDefinitionRepository & InMemoryRouteDefinitionRepository
RouteDefinitionRepository 接口中的方法用来对 RouteDefinition 进行增、删、查操作
- RouteDefinitionLocator 定义了用来获取路由定义列表的方法:
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions(); }
- RouteDefinitionWriter 定义了 写和删除的方法
public interface RouteDefinitionWriter { Mono<Void> save(Mono<RouteDefinition> route); Mono<Void> delete(Mono<String> routeId); }
RouteDefinitionRepository 接口负责将两个以上两个接口方法汇集:
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter { }
InMemoryRouteDefinitionRepository 实现了 RouteDefinitionRepository 接口,基于内存的路由定义仓库,将 路由信息存储再 内存中的 Map,提供服务的:读、写、删除
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository { /** * 用来存储路由信息的 Map */ private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap()); /** * 保存路由定义到内存中 * * @param route * @return */ @Override public Mono<Void> save(Mono<RouteDefinition> route) { return route.flatMap((r) -> { this.routes.put(r.getId(), r); return Mono.empty(); }); } /** * 根据路由id从内存中删除指定路由定义 * * @param routeId * @return */ @Override public Mono<Void> delete(Mono<String> routeId) { return routeId.flatMap((id) -> { if (this.routes.containsKey(id)) { this.routes.remove(id); return Mono.empty(); } else { return Mono.defer(() -> { return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)); }); } }); } /** * 获取内存中路由定义列表 * * @return */ @Override public Flux<RouteDefinition> getRouteDefinitions() { return Flux.fromIterable(this.routes.values()); } }
PropertiesRouteDefinitionLocator 基于配置属性的路由定义定位器
从配置文件 yaml或properties中读取路由配置信息,如代码所示:
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator { private final GatewayProperties properties; public PropertiesRouteDefinitionLocator(GatewayProperties properties) { this.properties = properties; } @Override public Flux<RouteDefinition> getRouteDefinitions() { return Flux.fromIterable(this.properties.getRoutes()); } }
DiscoveryClientRouteDefinitionLocator 基于服务发现的路由定义定位器
该类通过服务发现组件从注册中心获取服务信息,此时路由定义的源就是配置中心
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator { // 服务发现客户端 private final DiscoveryClient discoveryClient; // 服务发现属性 private final DiscoveryLocatorProperties properties; // 路由id前缀 private final String routeIdPrefix; // ------------------------------省略-------------------------------- } /** * 服务发现属性对象 */ @ConfigurationProperties("spring.cloud.gateway.discovery.locator") public class DiscoveryLocatorProperties { // 开启服务发现 private boolean enabled = false; // 路由前缀,默认为 discoveryClient. getClass(). getSimpleName() + "_". private String routeIdPrefix; // SpEL 表达式,判断网关是否集成一个服务,默认为 true private String includeExpression = "true"; // SpEL 表达式,为每个路由创建uri,默认为'lb://'+ serviceId private String urlExpression = "'lb://'+ serviceId"; // 在 断言 和 过滤器 中使用小写 serviceId,默认为 false private boolean lowerCaseServiceId = false; // 路由断言定义列表 private List<PredicateDefinition> predicates = new ArrayList(); //过滤器定义列表 private List<FilterDefinition> filters = new ArrayList(); // ------------------------------省略-------------------------------- }
在 DiscoveryLocatorProperties 定义了以上属性,要启用基于服务发现的路由定义定位器就必须设置
spring.cloud.gateway.discovery.locator.enabled= true
实操:(自定义路信息存储管理)
1、自定义一个路由信息描述类 存储路由信息的对象,继承 RouteDefinition 根据自己的需求可自行扩展增加属性字段(使用数据库对应字段,可自行灵活的扩展属性字段),数据库中存储此 类 对象
/** * 扩展此类支持序列化路由,(可将路由信息添加到数据库及Redis) */ @Data @EqualsAndHashCode(callSuper = false) public class RouteDefinitionInDb extends RouteDefinition implements Serializable { private static final long serialVersionUID = 1L; /** * 路由名称 */ private String routeName; }
2、路由数据的:增、删、读 的类(这里初始化加载路由信息也放在同一个类中处理,实际开发中可自行拆解,这里的示例 代码并没有处理 路由存储 MySQL 和 Redis ,读者请自行实现)
/** * 自定义路由系列化加载配置,负责对路由信息的读、写、删除的方法 */ @Slf4j @Component public class RouteDefinitionWriterAndReader implements RouteDefinitionRepository { /** * 这个是 Gateway 默认加载路由信息的配置(示例代码并没有使用Redis 和 MySQL ,读者自行变更为 MySQL 中存储,启动时加载到 Redis 中即可) */ @Autowired private GatewayProperties properties; /** * 后续改为 Redis 和 数据库方式存储 路由信息,此处改为相应的 RedisTemplate */ private static volatile Map<String, RouteDefinitionInDb> ROUTES_MAP = new ConcurrentHashMap<>(1 << 4); /** * 初始化配置文件中的路由到自定义容器中,后续直接从数据库中存储到Redis 去 */ @PostConstruct public void initRoute() { properties.getRoutes().forEach(route -> { RouteDefinitionInDb routeInDb = new RouteDefinitionInDb(); BeanUtil.copyProperties(route, routeInDb); // 暂时没有路由名称,ID代替 routeInDb.setRouteName("路由名称:" + route.getId()); log.info("加载路由:{},路由信息为:{}", routeInDb.getRouteName(), JSON.toJSONString(routeInDb)); ROUTES_MAP.put(route.getId(), routeInDb); }); log.info("路由加载完毕,路由数量为:{}", ROUTES_MAP.size()); } /** * 获取路由配置 * * @return */ @Override public Flux<RouteDefinition> getRouteDefinitions() { // RouteDefinition 后续可改为数据库存储的对象 RouteDefinitionInDb List<RouteDefinitionInDb> routesInDb = ROUTES_MAP.entrySet().stream() .map(routeEntry -> routeEntry.getValue()) .collect(Collectors.toList()); return Flux.fromIterable(routesInDb); } /** * 保存路由配置(暂时未实现具体逻辑) * * @param route * @return */ @Override public Mono<Void> save(Mono<RouteDefinition> route) { // 暂时使用默认方式配置,即使用配置文件读取的方式配置,后续改为Redis存储 Mono<Void> mono = route.flatMap(r -> { RouteDefinitionInDb routeInDb = new RouteDefinitionInDb(); BeanUtil.copyProperties(r, routeInDb); ROUTES_MAP.put(routeInDb.getId(), routeInDb); return Mono.empty(); }); return mono; } /** * 删除路由配置(暂时未实现具体逻辑) * * @param routeId * @return */ @Override public Mono<Void> delete(Mono<String> routeId) { // 时使用默认方式配置,即使用配置文件读取的方式配置,后续改为Redis存储 return routeId.flatMap(id -> { if (ROUTES_MAP.containsKey(id)) { ROUTES_MAP.remove(id); return Mono.empty(); } return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId))); }); } }