本文最后更新于 2023-07-06,文章内容可能已经过时。

目录结构

最主要的是红框部分的类

代码

定时任务

/**
 * @description: 定时任务 来控制刷新频率
 **/
@Component
public class ScheduledTask {
    private Logger logger = LoggerFactory.getLogger(ScheduledTask.class);
    
    private DynamicRouteService dynamicRouteService;
 
    @Autowired
    public void setDynamicRouteService(DynamicRouteService dynamicRouteService) {
        this.dynamicRouteService = dynamicRouteService;
    }
 
    @Scheduled(fixedDelay = 60000) // 每隔一分钟执行一次任务
    public void refreshRoutes() {
        dynamicRouteService.refreshRoutes();
    }
}

刷新路由

/**
 * @description: 动态路由实现类
 **/
@Service
public class DynamicRouteServiceImpl implements DynamicRouteService {
    private Logger logger = LoggerFactory.getLogger(DynamicRouteServiceImpl.class);

    @Resource
    private RouteDefinitionLocator routeDefinitionLocator;

    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    @Resource
    private ApplicationEventPublisher publisher;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Value("${dcii.app.service.app.rs.service.web}")
    private String RsServiceWebUrl;

    @Override
    public void refreshRoutes() {
        // 获取当前发布的路由信息并删除
        Iterable<RouteDefinition> routes = routeDefinitionLocator.getRouteDefinitions().toIterable();
        for (RouteDefinition route : routes) {
            // 这里是给id前面拼了web-,所以加了这个判断,否则直接删就好
            if(route.getId().contains("web-")){
                logger.info("【删除路由】{}",route.getId());
                routeDefinitionWriter.delete(Mono.just(route.getId()));
            }
        }
 
        // 从数据库加载最新的路由配置
        List<RouteDefinition> refreshedRoutes = loadRoutesFromDatabase();
 
        for (RouteDefinition route : refreshedRoutes) {
            logger.info("【添加路由】{}",route.getId());
            routeDefinitionWriter.save(Mono.just(route)).subscribe();
        }
        // 使更改立即生效
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public List<RouteDefinition> loadRoutesFromDatabase() {
        // 这里是调其他服务接口获取定义的路由(Uri)和断言(UriPattern)
        List<RsApplicationServiceTypeVo> rsApplicationServiceTypeVoList = getApplyService(RsServiceWebUrl);
        List<RouteDefinition> routeDefinitionList = new ArrayList<>();

        for (RsApplicationServiceTypeVo rsApplicationServiceTypeVo : rsApplicationServiceTypeVoList) {
            if(!StringUtils.hasText(rsApplicationServiceTypeVo.getUri()) || !StringUtils.hasText(rsApplicationServiceTypeVo.getUriPattern())
                    ) {
                continue;
            }

            RouteDefinition routeDefinition = new RouteDefinition();
            // 路由信息,将数据拼接成此格式{"name":"Path","args":{"pattern":"/**"}}
            List<Map<String, Object>> predicateList = new ArrayList<>();

            Map<String, Object> pathPredicateMap = new HashMap<>();
            pathPredicateMap.put("name", "Path");
            String uriPattern = rsApplicationServiceTypeVo.getUriPattern();
            // 只能固定到根路径,因为client请求的时候是localhost:8081?apiId=2,是以根路径开始的,所以不能加上servlet context path
            Map<String, String> patternMap = Collections.singletonMap("pattern", uriPattern);
            pathPredicateMap.put("args", patternMap);
            predicateList.add(pathPredicateMap);
            String predicateStr = null;
            List<PredicateDefinition> predicateDefinitions = null;
            try {
                predicateStr = objectMapper.writeValueAsString(predicateList);
                predicateDefinitions = Arrays.asList(objectMapper.readValue(predicateStr, PredicateDefinition[].class));

            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            List<Map<String, Object>> predicates = new ArrayList<>();
            Map<String, Object> filterMap = new HashMap<>();
            // 转发请求到真实地址前移除掉url中的apiId参数
            filterMap.put("name", "RemoveRequestParameter");
            Map<String, String> filterArgsMap = Collections.singletonMap("name", "apiId");
            filterMap.put("args", filterArgsMap);
            predicates.add(filterMap);
            // 过滤,没有用到
            List<FilterDefinition> filterDefinitions = null;
            try {
                String filterStr = objectMapper.writeValueAsString(predicates);
                filterDefinitions = Arrays.asList(objectMapper.readValue(filterStr, FilterDefinition[].class));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            // 组装
            routeDefinition.setId("web-" + rsApplicationServiceTypeVo.getAppUuid());
            routeDefinition.setOrder(1);
            routeDefinition.setPredicates(predicateDefinitions);
            routeDefinition.setFilters(filterDefinitions);
            try {
                routeDefinition.setUri(new URI(rsApplicationServiceTypeVo.getUri()));
            } catch (URISyntaxException e) {
                logger.info("设置{}的uri{}出错",rsApplicationServiceTypeVo.getAppUuid(),rsApplicationServiceTypeVo.getUri());
            }

            routeDefinitionList.add(routeDefinition);
        }
        return routeDefinitionList;
    }

    public List<RsApplicationServiceTypeVo> getApplyService(String applyServicePassedWebUrl){

//        logger.info("获取审批通过的服务信息接口:{}",applyServicePassedWebUrl);
        ResponseEntity responseEntity;
        List<RsApplicationServiceTypeVo> rsApplicationServiceTypeVoList = new ArrayList<>();
        try {
            responseEntity = restTemplate.getForObject(applyServicePassedWebUrl, ResponseEntity.class);
        } catch (Exception e) {
            logger.info("调用获取审批通过的服务信息接口失败{}",e.getMessage());
            return rsApplicationServiceTypeVoList;
        }
        Object bodyData = responseEntity.getData();
        try {
            // 转换报文
            rsApplicationServiceTypeVoList = objectMapper.readValue(objectMapper.writeValueAsString(bodyData), new TypeReference<List<RsApplicationServiceTypeVo>>() {});
        } catch (Exception e) {
            logger.info("转换报文出错{}",e.getMessage());
        }

        // 转换URL格式,只取出协议IP端口格式的数据
        Iterator<RsApplicationServiceTypeVo> iterator = rsApplicationServiceTypeVoList.iterator();
        while (iterator.hasNext()) {
            RsApplicationServiceTypeVo rsApplicationServiceTypeVo = iterator.next();
            if(StringUtils.hasText(rsApplicationServiceTypeVo.getUri())){
                String urlString = rsApplicationServiceTypeVo.getUri();
                try {
                    URL url = new URL(urlString);
                    String protocol = url.getProtocol();
                    String host = url.getHost();
                    int port = url.getPort();
                    String baseUrl = protocol + "://" + host + (port > 0 ? ":" + port : "");
                    rsApplicationServiceTypeVo.setUri(baseUrl);
                } catch (Exception e) {
                    logger.info("解析{}的URL出错{},剔除此条数据", rsApplicationServiceTypeVo.getAppUuid(), e.getMessage());
                    iterator.remove();
                }
            }
        }

        return rsApplicationServiceTypeVoList;
    }
}

打印日志

需要打印日志则在配置文件中添加

logging.level.org.springframework.cloud.gateway=debug
logging.level.org.springframework.http.server.reactive=debug
logging.level.org.springframework.web.reactive=debug
logging.level.org.springframework.boot.autoconfigure.web=debug
logging.level.reactor.netty=debug
logging.level.redisratelimiter=debug