package com.ruoyi.isc.utils; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import com.ruoyi.common.constant.IscConstants; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.isc.domain.IscAppService; import com.ruoyi.isc.domain.IscService; import com.ruoyi.isc.utils.beans.*; import com.ruoyi.isc.utils.beans.TopicMsg.Type; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RMap; import org.redisson.api.RTopic; import org.redisson.api.RedissonClient; import org.redisson.client.codec.Codec; import org.redisson.codec.TypedJsonJacksonCodec; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; /** * @author Wenchao Gong * @date 2021/9/10 14:46 */ @Slf4j public class RouteUtils { public static final Codec ROUTE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRouteDefinition.class); public static final Codec RULE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRule.class); public static final Codec TOPIC_MSG_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, TopicMsg.class); public static final String TOPIC_GATEWAY_REFRESH_ROUTE = "TOPIC_GATEWAY_REFRESH_ROUTE"; public static final String TOPIC_GATEWAY_RULE = "TOPIC_GATEWAY_RULE"; /** * Gateway 虚拟路径前缀 */ public static final String PATH_PREFIX = "/proxy"; public static final String ACCESS_KEY_NAME = "ak"; private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class); /** * 获取虚拟URL Path 部分 * * @param serviceAddr 服务地址 * @return 虚拟地址Path */ public static String genVirtualAddrPath(String serviceAddr) { return PATH_PREFIX + getPathUri(serviceAddr); } /** * 返回URI 的Path部分 * * @param uri URI * @return path */ public static String getPathUri(String uri) { return getURI(uri).getPath(); } /** * 通过 URI字符串 生成URI对象 * * @param uri URI字符串 * @return URI */ public static URI getURI(String uri) { try { return new URI(uri); } catch (URISyntaxException e) { throw new ServiceException("URL格式异常"); } } /** * 发布通道消息 * * @param channelKey 通道key * @param msg 发送数据 * @param consumer 自定义处理 */ public static void publish(String channelKey, T msg, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey, TOPIC_MSG_CODES_INSTANCE); topic.publish(msg); consumer.accept(msg); } /** * 发布路由刷新通知 * @param consumer 发送消息后执行 */ public static void sendRefreshRouteToGateway(String id, Type type, Consumer consumer) { publish(TOPIC_GATEWAY_REFRESH_ROUTE, new TopicMsg(id, DateUtil.now(), type), consumer); } /** * 发布规则通知 * @param consumer 发送消息后执行 */ public static void sendRuleNoticeToGateway(String id, Type type, Consumer consumer) { publish(TOPIC_GATEWAY_RULE, new TopicMsg(id, DateUtil.now(), type), consumer); } /** * 刷新路由信息 先清空再批量新增 * * @param routes 路由信息 * @return 是否成功 */ public static boolean refreshRoute(List routes) { Map routeMap = routes.stream().collect(Collectors.toMap(IscRouteDefinition::getId, Function.identity(), (o1, o2) -> o2)); final RMap map = CLIENT.getMap(IscConstants.KEY_ROUTES, ROUTE_CODES_INSTANCE); map.clear(); map.putAll(routeMap); sendRefreshRouteToGateway(null, Type.REFRESH, (msg) -> log.info("路由刷新完成,通知网关刷新路由![{}]", msg.getMsg())); return true; } /** * 保存路由信息 * * @param routes 路由信息 * @return 是否成功 */ public static boolean saveRoute(Collection routes) { return saveRoute(routes, null); } /** * 保存路由信息 * * @param routes 路由信息 * @param consumer 回调操作 * @return 是否成功 */ public static boolean saveRoute(Collection routes, Consumer consumer) { if(CollectionUtil.isEmpty(routes)) { return true; } final RMap map = CLIENT.getMap(IscConstants.KEY_ROUTES, ROUTE_CODES_INSTANCE); Map collect = routes.stream().collect(Collectors.toMap(IscRouteDefinition::getId, Function.identity())); map.putAll(collect); final String id = String.join(",", collect.keySet()); Type type = Type.UPDATE; if(Objects.isNull(consumer)) { type = Type.ADD; consumer = (msg) -> log.info("保存[{}]路由[{}]完成,通知网关刷新路由![{}]", msg.getType(), msg.getId(), msg.getMsg()); } sendRefreshRouteToGateway(id, type, consumer); return true; } /** * 删除路由信息 * * @param routeIds 路由ID * @return 是否成功 */ public static boolean deleteRoute(Collection routeIds) { final RMap map = CLIENT.getMap(IscConstants.KEY_ROUTES, ROUTE_CODES_INSTANCE); final String[] ids = routeIds.toArray(new String[0]); map.fastRemove(ids); final String id = String.join(",", routeIds); sendRefreshRouteToGateway(id, Type.DELETE, (msg) -> log.info("删除[{}]路由[{}]完成,通知网关刷新路由![{}]", msg.getType(), msg.getId(), msg.getMsg())); return true; } /** * 更新路由信息 * * @param route 路由信息 * @return 是否成功 */ public static boolean updateRoute(IscRouteDefinition route) { return saveRoute(Collections.singletonList(route), (msg) -> log.info("保存[{}]路由[{}]完成,通知网关刷新路由![{}]", msg.getType(), msg.getId(), msg.getMsg())); } /** * 生成 路由信息 * * @param service 服务信息 * @return 路由信息 */ public static IscRouteDefinition generateRoute(IscService service) { IscRouteDefinition route = new IscRouteDefinition(); route.setId(String.valueOf(service.getServiceId())); final String serviceAddr = service.getServiceAddr(); route.setUri(getURI(serviceAddr.substring(0, serviceAddr.length() - getPathUri(serviceAddr).length()))); //断言 IscPredicateDefinition methodPredicate = new IscPredicateDefinition(); methodPredicate.setName("Method"); methodPredicate.getArgs().put("methods", service.getRequestMethod()); IscPredicateDefinition pathPredicate = new IscPredicateDefinition(); pathPredicate.setName("Path"); pathPredicate.getArgs().put("pattern", genVirtualAddrPath(service.getServiceAddr())); route.setPredicates(Arrays.asList(methodPredicate, pathPredicate)); //过滤器 List filters = new ArrayList<>(); IscFilterDefinition stripPrefixFilter = new IscFilterDefinition(); stripPrefixFilter.setName("StripPrefix"); stripPrefixFilter.getArgs().put("parts", "1"); filters.add(stripPrefixFilter); IscFilterDefinition retryFilter = new IscFilterDefinition(); retryFilter.setName("Retry"); retryFilter.getArgs().put("retries", "1"); filters.add(retryFilter); route.setFilters(filters); //其他信息 Map metadata = route.getMetadata(); metadata.put("accessKeyName", ACCESS_KEY_NAME); metadata.put("addParam", service.getHiddenParams()); return route; } /** * 生成 服务对应AK调用规则 * * @param appService 服务申请信息 * @param accessKey AccessKey * @return 规则信息 */ public static IscRule generateRule(IscAppService appService, String accessKey) { IscRule rule = new IscRule(); rule.setId(accessKey + ':' + appService.getServiceId()); rule.setExpire(appService.getEndTime()); rule.setDaysLimit(appService.getQuotaDays()); rule.setHoursLimit(appService.getQuotaHours()); rule.setMinutesLimit(appService.getQuotaMinutes()); rule.setSecondsLimit(appService.getQuotaSeconds()); return rule; } /** * 刷新规则信息 先清空再批量新增 * * @param rules 规则信息 * @return 是否成功 */ public static boolean refreshRules(List rules) { Map ruleMap = rules.stream().collect(Collectors.toMap(IscRule::getId, Function.identity(), (o1, o2) -> o2)); final RMap map = CLIENT.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE); map.clear(); map.putAll(ruleMap); final String id = String.join(",", ruleMap.keySet()); sendRuleNoticeToGateway(id, Type.REFRESH, (msg) -> log.info("刷新[{}]所有规则[{}]完成,通知网关刷新规则![{}]", msg.getType(), msg.getId(), msg.getMsg())); return true; } /** * 保存规则信息 * * @param rule 规则信息 * @return 是否成功 */ public static boolean saveRule(IscRule rule) { return saveRule(rule, null); } /** * 保存规则信息 * * @param rule 规则信息 * @param consumer 回调 * @return 是否成功 */ public static boolean saveRule(IscRule rule, Consumer consumer) { final RMap map = CLIENT.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE); map.put(rule.getId(), rule); Type type = Type.UPDATE; if(Objects.isNull(consumer)) { type = Type.ADD; consumer = (msg) -> log.info("新增[{}]规则[{}]完成,通知网关刷新规则![{}]", msg.getType(), msg.getId(), msg.getMsg()); } sendRuleNoticeToGateway(rule.getId(), type, consumer); return true; } /** * 删除规则信息 * * @param ruleId 规则ID * @return 是否成功 */ public static boolean deleteRule(String ruleId) { final RMap map = CLIENT.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE); map.remove(ruleId); sendRuleNoticeToGateway(ruleId, Type.DELETE, (msg) -> log.info("删除[{}]规则[{}]完成,通知网关刷新规则![{}]", msg.getType(), msg.getId(), msg.getMsg())); return true; } /** * 更新规则信息 * * @param rule 规则信息 * @return 是否成功 */ public static boolean updateRule(IscRule rule) { return saveRule(rule, (msg) -> log.info("更新[{}]规则[{}]完成,通知网关刷新规则![{}]", msg.getType(), msg.getId(), msg.getMsg())); } }