Spring Cloud Gateway实现H5动态网关
本文最后更新于 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
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 周日
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果