Spring Cloud Gateway之动态路由(数据库版)
1、实现动态路由的关键是RouteDefinitionRepository接口,该接口存在一个默认实现(InMemoryRouteDefinitionRepository)
通过名字我们应该也知道该实现是将配置文件中配置的信息加载到内存中。因此无法实现动态路由。
2、如果想实现动态路由,我们可以参考默认实现,自己编写一个实现,代码如下:
import static java.util.Collections.synchronizedMap; @Component public class UnifiedRouteRepositoryImpl implements RouteDefinitionRepository { public final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<>()); @Override public Flux<RouteDefinition> getRouteDefinitions() { return Flux.fromIterable(routes.values()); } @Override public Mono<Void> save(Mono<RouteDefinition> route) { return route.flatMap( r -> { routes.put(r.getId(), r); return Mono.empty(); }); } @Override public Mono<Void> delete(Mono<String> routeId) { return routeId.flatMap(id -> { if (routes.containsKey(id)) { routes.remove(id); return Mono.empty(); } return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: "+routeId))); }); } }
3、虽然我们模仿默认实现,实现了自己的路由处理,但是我们还存在几个问题,就是如何将数据库中的数据加载到Map<String, RouteDefinition> routes =synchronizedMap(new LinkedHashMap<>());
还有就是当数据库数据变化时,如何刷新routes?
CommandLineRunner 作用当项目启动以后调用该类中的run(String... args)方法
ApplicationEventPublisherAware 可以理解为观察者模式,当存在数据变化时,就刷新缓存中的路由
@Slf4j
@Component
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {
@Autowired @Qualifier(value = "unifiedRouteRepositoryImpl") private RouteDefinitionWriter routeDefinitionWriter;
//该类需要你自己编写(将数据库中的动态路由加载到内存中) @Autowired private RouteRepository routeRepository; private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } @Override public void run(String... args){ this.loadRouteConfig(); } public void loadRouteConfig() { List<Map<String, Object>> lists = routeRepository.getListAll(); lists.forEach(r ->{ RouteDefinition route = new RouteDefinition(); PredicateDefinition predicate = new PredicateDefinition(); Map<String, String> predicateParams = new HashMap<>(2); FilterDefinition filter = new FilterDefinition(); Map<String, String> filterParams = new HashMap<>(2); //设置Id route.setId((String)r.get("routeId")); try { route.setUri(new URI((String)r.get("uri"))); } catch (URISyntaxException e) { e.printStackTrace(); } predicate.setName((String)r.get("predicateName")); predicateParams.put("pattern",(String)r.get("predicateArgs")); predicate.setArgs(predicateParams); if(!StringUtils.isBlank(predicate.getName())){ route.setPredicates(Arrays.asList(predicate)); } filter.setName((String)r.get("filterName")); filterParams.put("_genkey_0",(String)r.get("filterArgs")); filter.setArgs(filterParams); if(!StringUtils.isBlank(filter.getName())){ route.setFilters(Arrays.asList(filter)); } routeDefinitionWriter.save(Mono.just(route)).subscribe(); }); this.publisher.publishEvent(new RefreshRoutesEvent(this)); } public void deleteRoute(String routeId){ routeDefinitionWriter.delete(Mono.just(routeId)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); }