public interface RouteDefinitionRepository
extends RouteDefinitionLocator, RouteDefinitionWriter {
}
RouteDefinitionRepository是网关路由的存储接口,RouteDefinitionLocator 是获取存储中的所有路由,RouteDefinitionWriter主要操作路由的存储和删除。
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
public interface RouteDefinitionWriter {
Mono<Void> save(Mono<RouteDefinition> route);
Mono<Void> delete(Mono<String> routeId);
}
而gateway中RouteDefinitionRepository接口的默认的实现是InMemoryRouteDefinitionRepository,即在内存中存储路由配置,而且在 GatewayAutoConfiguration 配置中也激活了InMemoryRouteDefinitionRepository这个Bean,代码如下。
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = synchronizedMap(
new LinkedHashMap<String, RouteDefinition>());
@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)));
});
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(routes.values());
}
}
InMemoryRouteDefinitionRepository 中可见存储路由的是一个带同步锁的LinkedHashMap,而存储删除都是基于这个map对象操作。
@Slf4j
@Configuration("redisRouteDefinition")
@AllArgsConstructor
public class InRedisRouteDefinitionRepository implements RouteDefinitionRepository {
private RedisTemplate redisTemplate;
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(r -> {
redisTemplate.opsForHash().put(DynamicRouterConstants.DYNAMIC_ROUTER_KEY_CONFIG, r.getId(), new Gson().toJson(r));
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id -> {
Object router = redisTemplate.opsForHash().get(DynamicRouterConstants.DYNAMIC_ROUTER_KEY_CONFIG, id);
if (!Objects.isNull(router)) {
redisTemplate.opsForHash().delete(DynamicRouterConstants.DYNAMIC_ROUTER_KEY_CONFIG, id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(
new NotFoundException("RouteDefinition not found: " + routeId)));
});
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<String> values = redisTemplate.opsForHash().values(DynamicRouterConstants.DYNAMIC_ROUTER_KEY_CONFIG);
if (CollUtil.isNotEmpty(values)) {
List<RouteDefinition> definitions = values.stream()
.map(s -> new Gson().fromJson(s, RouteDefinition.class))
.collect(Collectors.toList());
return Flux.fromIterable(definitions);
} else {
return Flux.fromIterable(new ArrayList<>());
}
}
}
暂时在网关中提供接口实现路由的动态增加和修改Controller
@RestController
@RequestMapping("/route")
@AllArgsConstructor
public class RouteController {
private DynamicRouteService dynamicRouteService;
@PostMapping
public void saveRouteDefinition(@RequestBody GatewayRouteDefinition routeDefinition) {
dynamicRouteService.saveRouteDefinition(routeDefinition);
}
@DeleteMapping("/{id}")
public void deleteRouteDefinition(@PathVariable String id) {
dynamicRouteService.deleteRouteDefinition(id);
}
@PutMapping
public void update(@RequestBody GatewayRouteDefinition routeDefinition) {
dynamicRouteService.updateRouteDefinition(routeDefinition);
}
@GetMapping
public IPage<RouterConfig> getRouterConfigByPage(RouterConfigQueryParams params) {
return dynamicRouteService.getRouterConfigByPage(params);
}
}
路由参数
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class GatewayRouteDefinition {
/**
* 路由的Id
*/
private String id;
/**
* 路由断言集合配置
*/
private List<GatewayPredicateDefinition> predicates;
/**
* 路由过滤器集合配置
*/
private List<GatewayFilterDefinition> filters;
/**
* 路由规则转发的目标uri
*/
private String uri;
/**
* 路由执行的顺序
*/
private int order;
}
@Data
public class GatewayPredicateDefinition implements Serializable {
/**
* 断言对应的Name
*/
private String name;
/**
* 配置的断言规则
*/
private Map<String, String> args = new LinkedHashMap<>();
}
@Data
public class GatewayFilterDefinition implements Serializable {
/**
* Filter Name
*/
private String name;
/**
* 对应的路由规则
*/
private Map<String, String> args = new LinkedHashMap<>();
}
业务层代码 DynamicRouteService,最主要的是注入RouteDefinitionWriter 我们自己的实现类,替换默认的配置
@Service
public class DynamicRouteServiceImpl implements DynamicRouteService {
@Resource(name = "redisRouteDefinition")
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private IRouterConfigService routerConfigService;
@Autowired
private ObjectMapper objectMapper;
@Override
public void saveRouteDefinition(GatewayRouteDefinition definition) {
// 判定当前路由以及路径是否存在
LambdaQueryWrapper<RouterConfig> wrapper = Wrappers.<RouterConfig>lambdaQuery()
.eq(RouterConfig::getRouterName, definition.getId())
.eq(RouterConfig::getRouterPath, definition.getUri());
List<RouterConfig> list = routerConfigService.list(wrapper);
BizVerify.verify(CollUtil.isEmpty(list), "路由已经存在");
routerConfigService.save(paramsConvert(definition));
RouteDefinition routerDefinition = DynamicRouteUtils.convertToRouteDefinition(definition);
routeDefinitionWriter.save(Mono.just(routerDefinition)).subscribe();
}
@Override
public void updateRouteDefinition(GatewayRouteDefinition routeDefinition) {
routerConfigService.updateById(paramsConvert(routeDefinition));
RouteDefinition definition = DynamicRouteUtils.convertToRouteDefinition(routeDefinition);
deleteRouteDefinition(definition.getId());
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
}
@Override
public void deleteRouteDefinition(String routerId) {
routerConfigService.removeById(routerId);
routeDefinitionWriter
.delete(Mono.just(routerId))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume((t) -> t instanceof NotFoundException, (t) -> Mono.just(ResponseEntity.notFound().build()));
}
private RouterConfig paramsConvert(GatewayRouteDefinition routeDefinition) {
String filterJson = null;
String PredicatesJson = null;
try {
filterJson = objectMapper.writeValueAsString(routeDefinition.getFilters());
PredicatesJson = objectMapper.writeValueAsString(routeDefinition.getPredicates());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return new RouterConfig()
.setRouterName(routeDefinition.getId())
.setRouterPath(routeDefinition.getUri())
.setRouterOrder(routeDefinition.getOrder())
.setRouterFilters(filterJson)
.setRouterPredicates(PredicatesJson);
}
@Override
public IPage<RouterConfig> getRouterConfigByPage(RouterConfigQueryParams params) {
LambdaQueryWrapper<RouterConfig> wrapper = Wrappers.<RouterConfig>lambdaQuery()
.like(StrUtil.isNotEmpty(params.getRouterName()), RouterConfig::getRouterName, params.getRouterName());
return routerConfigService.page(new Page<>(params.getPageNum(), params.getPageSize()), wrapper);
}
}
package com.kill.core.provider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/**
*
* +--------+---------+-----------+---------+
* | |
* +--------+---------+-----------+---------+
*
*
* @author wangjian
* @since 1019/11/01 11:58:32
*/
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceProvider implements SwaggerResourcesProvider {
private static final String SWAGGER2URL = "/v2/api-docs";
private RouteDefinitionRepository repository;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
repository.getRouteDefinitions().subscribe(
route -> {
if (CollUtil.isNotEmpty(route.getPredicates())) {
route.getPredicates().forEach(
predicateDefinition -> {
if (CollUtil.isNotEmpty(predicateDefinition.getArgs())) {
if (StrUtil.isNotEmpty(predicateDefinition.getArgs().get("pattern"))) {
resources.add(
swaggerResource(route.getId(),
predicateDefinition.getArgs().get("pattern").replace("/**", SWAGGER2URL)));
}
if (StrUtil.isNotEmpty(predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0"))) {
resources.add(
swaggerResource(route.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)));
}
}
});
}
});
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}