• Spring Cloud Gateway 数据库存储路由信息的扩展方案


    动态路由背景

    无论你在使用Zuul还是Spring Cloud Gateway 的时候,官方文档提供的方案总是基于配置文件配置的方式

    例如:

      # zuul 的配置形式
      routes:
        pig-auth:
          path: /auth/**
          serviceId: pig-auth
          stripPrefix: true
      # gateway 的配置形式
      routes:
      - id: pigx-auth
      	uri: lb://pigx-auth
        predicates:
        - Path=/auth/**
        filters:
        - ValidateCodeGatewayFilter

    配置更改需要重启服务,不能满足实际生产过程中的动态刷新、实时变更的业务需求。

    基于以上分析 **pig**已经提供了基于Zuul版本的动态路由功能,附[Git 地址传送门](https://gitee.com/log4j/pig),效果如下图可以实时配置修改刷新。

    Spring Cloud Gateway 路由加载源码

    1. DispatcherHandler 接管用户请求
    2. RoutePredicateHandlerMapping 路由匹配
      1. 根据RouteLocator获取 RouteDefinitionLocator
      2. 返回多个RouteDefinitionLocator.getRouteDefinitions()的路由定义信息
    3. FilteringWebHandler执行路由定义中的filter 最后路由到具体的业务服务中

    Spring Cloud Gateway 默认动态路由实现

    GatewayControllerEndpoint 基于actuate端点的默认实现,支持JVM 级别的动态路由,不能序列化存储

    // 上图动态路由的信息保存的默认实现是基于内存的实现
    public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
    	private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
    	@Override
    	public Mono<Void> save(Mono<RouteDefinition> route){}
    	@Override
    	public Mono<Void> delete(Mono<String> routeId){}
    
    	@Override
    	public Flux<RouteDefinition> getRouteDefinitions(){}
    }

    扩展基于Mysql + Redis存储分布式动态组件

    为什么使用Mysql的同时,又要使用Redis?

    1. spring cloud gateway 基于webflux 背压,暂时不支持mysql 数据库
    2. Redis-reactive 支持 spring cloudgateway 的背压,同时还可以实现分布式,高性能

    扩展思路

    1. 增加一个路由管理模块,参考GatewayControllerEndpoint实现,启动时加载数据库中配置文件到Redis
    2. 网关模块重写RouteDefinitionRepository,getRouteDefinitions()取Redis中读取即可实现
    3. 前端配合 json-view 类似插件,直接修改展示。

    具体实现

    1. 路由管理模块核心处理逻辑,获取路由和更新路由/** * @author lengleng * @date 2018年11月06日10:27:55 * <p> * 动态路由处理类 */ @Slf4j @AllArgsConstructor @Service("sysRouteConfService") public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, SysRouteConf> implements SysRouteConfService { private final RedisTemplate redisTemplate; private final ApplicationEventPublisher applicationEventPublisher; /** * 获取全部路由 * <p> * RedisRouteDefinitionWriter.java * PropertiesRouteDefinitionLocator.java * * @return */ @Override public List<SysRouteConf> routes() { SysRouteConf condition = new SysRouteConf(); condition.setDelFlag(CommonConstant.STATUS_NORMAL); return baseMapper.selectList(new EntityWrapper<>(condition)); } /** * 更新路由信息 * * @param routes 路由信息 * @return */ @Override public Mono<Void> editRoutes(JSONArray routes) { // 清空Redis 缓存 Boolean result = redisTemplate.delete(CommonConstant.ROUTE_KEY); log.info("清空网关路由 {} ", result); // 遍历修改的routes,保存到Redis List<RouteDefinitionVo> routeDefinitionVoList = new ArrayList<>(); routes.forEach(value -> { log.info("更新路由 ->{}", value); RouteDefinitionVo vo = new RouteDefinitionVo(); Map<String, Object> map = (Map) value; Object id = map.get("routeId"); if (id != null) { vo.setId(String.valueOf(id)); } Object predicates = map.get("predicates"); if (predicates != null) { JSONArray predicatesArray = (JSONArray) predicates; List<PredicateDefinition> predicateDefinitionList = predicatesArray.toList(PredicateDefinition.class); vo.setPredicates(predicateDefinitionList); } Object filters = map.get("filters"); if (filters != null) { JSONArray filtersArray = (JSONArray) filters; List<FilterDefinition> filterDefinitionList = filtersArray.toList(FilterDefinition.class); vo.setFilters(filterDefinitionList); } Object uri = map.get("uri"); if (uri != null) { vo.setUri(URI.create(String.valueOf(uri))); } Object order = map.get("order"); if (order != null) { vo.setOrder(Integer.parseInt(String.valueOf(order))); } redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class)); redisTemplate.opsForHash().put(CommonConstant.ROUTE_KEY, vo.getId(), vo); routeDefinitionVoList.add(vo); }); // 逻辑删除全部 SysRouteConf condition = new SysRouteConf(); condition.setDelFlag(CommonConstant.STATUS_NORMAL); this.delete(new EntityWrapper<>(condition)); //插入生效路由 List<SysRouteConf> routeConfList = routeDefinitionVoList.stream().map(vo -> { SysRouteConf routeConf = new SysRouteConf(); routeConf.setRouteId(vo.getId()); routeConf.setFilters(JSONUtil.toJsonStr(vo.getFilters())); routeConf.setPredicates(JSONUtil.toJsonStr(vo.getPredicates())); routeConf.setOrder(vo.getOrder()); routeConf.setUri(vo.getUri().toString()); return routeConf; }).collect(Collectors.toList()); this.insertBatch(routeConfList); log.debug("更新网关路由结束 "); this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); return Mono.empty(); } }
    2. 网关自定义RedisRouteDefinitionRepository
    
    

    @Slf4j

    @Component

    @AllArgsConstructor

    public class RedisRouteDefinitionWriter implements RouteDefinitionRepository {

    private final RedisTemplate redisTemplate;

    @Override

    public Mono<Void> save(Mono<RouteDefinition> route) {

    return route.flatMap(r -> {

    RouteDefinitionVo vo = new RouteDefinitionVo();

    BeanUtils.copyProperties(r, vo);

    log.info("保存路由信息{}", vo);

    redisTemplate.opsForHash().put(CommonConstant.ROUTE_KEY, r.getId(), vo);

    return Mono.empty();

    });

    }

    @Override

    public Mono<Void> delete(Mono<String> routeId) {

    routeId.subscribe(id -> {

    log.info("删除路由信息{}", id);

    redisTemplate.opsForHash().delete(CommonConstant.ROUTE_KEY, id);

    });

    return Mono.empty();

    }

    @Override

    public Flux<RouteDefinition> getRouteDefinitions() {

    redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));

    List<RouteDefinitionVo> values = redisTemplate.opsForHash().values(CommonConstant.ROUTE_KEY);

    List<RouteDefinition> definitionList = new ArrayList<>();

    values.forEach(vo -> {

    RouteDefinition routeDefinition = new RouteDefinition();

    BeanUtils.copyProperties(vo, routeDefinition);

    definitionList.add(vo);

    });

    log.debug("redis 中路由定义条数: {}, {}", definitionList.size(), definitionList);

    return Flux.fromIterable(definitionList);

    }

    }

    
    

    3.库表定义

    转自:https://cloud.tencent.com/developer/article/1361901

  • 相关阅读:
    destoon短信接口修改方法
    关于微信公众平台模拟登录
    PHP中基本符号及使用方法
    day03-集合
    day02-字符串、字典
    day02-购物车程序练习
    day02-模块、数据类型、三元运算、列表、元组
    day01-if else、while、for、continue、break
    day01-变量、字符编码、交互输入
    通过chrome识别手机端app元素--Chrome:inspector
  • 原文地址:https://www.cnblogs.com/javalinux/p/14377470.html
Copyright © 2020-2023  润新知