gateway配置路由的主要方式有两种,一种是用yaml文件,一种是写代码里,这两种方式都不支持动态路由配置。如:
public RouteLocator coustomerRouteLocator(RouteLocatorBuilder builder){return builder.routes().route("id-1", r->r.path("/**").uri("lb://SERVER-DEMO")).build();}
cloud:gateway:routes:- id: header_routeuri: https://example.orgpredicates:- Header=X-Request-Id, \d+
下面就来看看gateway是如何来加载这些配置信息的。
一、路由初始化
无论是yaml还是代码,这些配置最终都是封装到RouteDefinition对象中。

一个RouteDefinition有个唯一的ID,如果不指定,就默认是UUID,多个RouteDefinition组成了gateway的路由系统。
所有路由信息在系统启动时就被加载装配好了,并存到了内存里。我们从源码来看看。

圆圈里就是装配yml文件的,它返回的是PropertiesRouteDefinitionLocator,该类继承了RouteDefinitionLocator,RouteDefinitionLocator就是路由的装载器,里面只有一个方法,就是获取路由信息的。该接口有多个实现类,分别对应不同方式配置的路由方式。
public interface RouteDefinitionLocator {Flux<RouteDefinition> getRouteDefinitions();}
通过这几个实现类,再结合上面的AutoConfiguration里面的Primary信息,就知道加载配置信息的顺序。
PropertiesRouteDefinitionLocator-->|配置文件加载初始化| CompositeRouteDefinitionLocator
RouteDefinitionRepository-->|存储器中加载初始化| CompositeRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator-->|注册中心加载初始化| CompositeRouteDefinitionLocator

这是第一顺序,就是从CachingRouteLocator中获取路由信息,我们可以打开该类进行验证。

不管发起什么请求,必然会走上面的断点处。请求一次,走一次。这是将路由信息缓存到了Map中。配置信息一旦请求过一次,就会被缓存到上图的CachingRouteLocator类中,再次发起请求后,会直接从map中读取。
如果想动态刷新配置信息,就需要发起一个RefreshRoutesEvent的事件,上图的cache会监听该事件,并重新拉取路由配置信息。
通过下图,可以看到如果没有RouteDefinitionRepository的实例,则默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。即通过自定义的RouteDefinitionRepository类,来提供路由配置信息。

二、基于数据库、缓存的动态路由
核心实现类:
1@Component
2public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
3
4 public static final String GATEWAY_ROUTES = "geteway_routes";
5
6 @Resource
7 private StringRedisTemplate redisTemplate;
8
9 @Override
10 public Flux<RouteDefinition> getRouteDefinitions() {
11 List<RouteDefinition> routeDefinitions = new ArrayList<>();
12 redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
13 .forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class)));
14 return Flux.fromIterable(routeDefinitions);
15 }
16
17 @Override
18 public Mono<Void> save(Mono<RouteDefinition> route) {
19 return route.flatMap(r -> {
20 redisTemplate.opsForHash().put(GATEWAY_ROUTES, r.getId(), JSON.toJSONString(r));
21 return Mono.empty();
22 });
23 }
24
25 @Override
26 public Mono<Void> delete(Mono<String> routeId) {
27 return routeId.flatMap(id ->{
28 redisTemplate.opsForHash().delete(GATEWAY_ROUTES, id);
29 return Mono.empty();
30 });
31 }
32
33}
主要是在get方法里,此处从redis里获取配置好的Definition。
然后我们的工作就是将配置信息,放到redis里即可。
三、通过REST接口配置网关
我们可以定义一个rest接口来对网关进行动态配置,包含查询、删除、修改操作。
1@RestController
2public class RouteController {
3
4 @Autowired
5 private DynamicRouteService dynamicRouteService;
6
7 @RequestMapping("/getall")
8 public Flux<RouteDefinition> getRoutes(){
9 return dynamicRouteService.getAllRoute();
10 }
11
12 @DeleteMapping("/delete/{id}")
13 public String deleteRoute(@PathVariable String id){
14 return dynamicRouteService.delete(id);
15 }
16
17 @PostMapping("/add")
18 public String insertOrUpdate(@RequestBody RouteDefinition routeDefinition){
19 return dynamicRouteService.add(routeDefinition);
20 }
21
22}
每次对网关操作完成后,都需要通知gateway进行主动刷新。刷新方式是发出RefreshRoutesEvent事件。

完整的增删改查service代码如下:
1@Service
2public class DynamicRouteService implements ApplicationEventPublisherAware {
3
4 @Resource
5 private RouteDefinitionRepository routeDefinitionWriter;
6
7 private ApplicationEventPublisher publisher;
8
9 private void notifyChanged() {
10 this.publisher.publishEvent(new RefreshRoutesEvent(this));
11 }
12
13 public Flux<RouteDefinition> getAllRoute(){
14 return routeDefinitionWriter.getRouteDefinitions();
15 }
16
17
18
19 /**
20 * 增加路由
21 *
22 */
23 public String add(RouteDefinition definition) {
24 routeDefinitionWriter.save(Mono.just(definition)).subscribe();
25 notifyChanged();
26 return "success";
27 }
28
29
30 /**
31 * 更新路由
32 */
33 public String update(RouteDefinition definition) {
34 try {
35 this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
36 } catch (Exception e) {
37 return "update fail,not find route routeId: " + definition.getId();
38 }
39 try {
40 routeDefinitionWriter.save(Mono.just(definition)).subscribe();
41 notifyChanged();
42 return "success";
43 } catch (Exception e) {
44 return "update route fail";
45 }
46
47
48 }
49
50 /**
51 * 删除路由
52 *
53 */
54 public String delete(String id) {
55 try {
56 this.routeDefinitionWriter.delete(Mono.just(id));
57
58 notifyChanged();
59 return "delete success";
60 } catch (Exception e) {
61 e.printStackTrace();
62 return "delete fail";
63 }
64
65 }
66
67 @Override
68 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
69 this.publisher = applicationEventPublisher;
70 }
71
72}
完成之后我们就可以用postman进行增删改查。

或使用curl进行测试:
curl -H "Content-Type:application/json" -X POST --data '{"filters":[],"id":"1","order":0,"predicates":[{"args":{"pattern":"/**"},"name":"Path"}],"uri":"lb://SERVER-DEMO"}' http://127.0.0.1:8080/add
代码下载
https://github.com/loveoobaby/blog_code/tree/master/spring_gateway
原文地址:
https://blog.csdn.net/tianyaleixiaowu/article/details/83412301




