网关动态路由原理

网关动态路由原理

PIGCLOUD

📄 网关动态路由原理 _ PIGCLOUD

](https://www.pig4cloud.com/)

产品

商业版

生态🔥

📄 网关动态路由原理

pigcloud

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

# Spring Cloud Gateway 路由加载源码

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

# Spring Cloud Gateway 默认动态路由实现

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 上图动态路由的信息保存的默认实现是基于内存的实现
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(){}
}




Copied!

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

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

  1. spring cloud gateway 基于 webflux 背压,暂时不支持 mysql 数据库

  2. Redis-reactive 支持 spring cloudgateway 的背压,同时还可以实现分布式,高性能

    # 扩展思路

1621002990

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

# 具体实现

    1. 路由管理模块核心处理逻辑,获取路由和更新路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**
* @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();
}
}






Copied!
    1. 网关自定义 RedisRouteDefinitionRepository
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    /**
    * @author lengleng
    * @date 2018/10/31
    * <p>
    * redis 保存路由信息,优先级比配置文件高
    */
    @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();
    }

    /**
    * 动态路由入口
    *
    * @return
    */
    @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);
    }
    }




    Copied!
  • 3.库表定义

📄 配置文件动态刷新 📄 服务配置 cors 跨域

  • 标题: 网关动态路由原理
  • 作者: PIGCLOUD
  • 创建于 : 2024-01-01 00:00:00
  • 更新于 : 2025-09-22 14:27:28
  • 链接: https://anime-blog.52lin.site/📄 网关动态路由原理 _ PIGCLOUD/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。