当前位置:   article > 正文

详解SpringCloud-gateway动态路由两种方式,以及路由加载过程_compositeroutedefinitionlocator

compositeroutedefinitionlocator

gateway配置路由主要有两种方式,一种是用yml配置文件,一种是写代码里,这两种方式都是不支持动态配置的。如:

下面就来看看gateway是如何加载这些配置信息的。

1 路由初始化

无论是yml还是代码,这些配置最终都是被封装到RouteDefinition对象中。

一个RouteDefinition有个唯一的ID,如果不指定,就默认是UUID,多个RouteDefinition组成了gateway的路由系统。

所有路由信息在系统启动时就被加载装配好了,并存到了内存里。我们从源码来看看。

圆圈里就是装配yml文件的,它返回的是PropertiesRouteDefinitionLocator,该类继承了RouteDefinitionLocator,RouteDefinitionLocator就是路由的装载器,里面只有一个方法,就是获取路由信息的。该接口有多个实现类,分别对应不同方式配置的路由方式。

通过这几个实现类,再结合上面的AutoConfiguration里面的Primary信息,就知道加载配置信息的顺序。

PropertiesRouteDefinitionLocator-->|配置文件加载初始化| CompositeRouteDefinitionLocator
RouteDefinitionRepository-->|存储器中加载初始化| CompositeRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator-->|注册中心加载初始化| CompositeRouteDefinitionLocator

参考:https://www.jianshu.com/p/b02c7495eb5e

https://blog.csdn.net/X5fnncxzq4/article/details/80221488

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

不管发起什么请求,必然会走上面的断点处。请求一次,走一次。这是将路由信息缓存到了Map中。配置信息一旦请求过一次,就会被缓存到上图的CachingRouteLocator类中,再次发起请求后,会直接从map中读取。

如果想动态刷新配置信息,就需要发起一个RefreshRoutesEvent的事件,上图的cache会监听该事件,并重新拉取路由配置信息。

通过下图,可以看到如果没有RouteDefinitionRepository的实例,则默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。即通过自定义的RouteDefinitionRepository类,来提供路由配置信息。

例如:

在getRouteDefinitions方法返回你自定义的路由配置信息即可。这里可以用数据库、nosql等等任意你喜欢的方式来提供。而且配置信息修改后,发起一次RefreshRoutesEvent事件即可让配置生效。这就是动态配置路由的核心所在,下面来看具体代码实现。

2 基于数据库、缓存的动态路由

pom.xml如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.maimeng</groupId>
  6. <artifactId>apigateway</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>apigateway</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-parent</artifactId>
  14. <version>2.0.6.RELEASE</version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. <java.version>1.8</java.version>
  21. <spring-cloud.version>Finchley.SR1</spring-cloud.version>
  22. </properties>
  23. <dependencies>
  24. <dependency>
  25. <groupId>org.springframework.cloud</groupId>
  26. <artifactId>spring-cloud-starter-gateway</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-webflux</artifactId>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-actuator</artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.springframework.boot</groupId>
  38. <artifactId>spring-boot-starter-data-redis</artifactId>
  39. </dependency>
  40. <dependency>
  41. <groupId>com.alibaba</groupId>
  42. <artifactId>fastjson</artifactId>
  43. <version>1.2.51</version>
  44. </dependency>
  45. <!--<dependency>
  46. <groupId>mysql</groupId>
  47. <artifactId>mysql-connector-java</artifactId>
  48. <scope>runtime</scope>
  49. </dependency>-->
  50. <dependency>
  51. <groupId>org.springframework.boot</groupId>
  52. <artifactId>spring-boot-starter-test</artifactId>
  53. <scope>test</scope>
  54. </dependency>
  55. </dependencies>
  56. <dependencyManagement>
  57. <dependencies>
  58. <dependency>
  59. <groupId>org.springframework.cloud</groupId>
  60. <artifactId>spring-cloud-dependencies</artifactId>
  61. <version>${spring-cloud.version}</version>
  62. <type>pom</type>
  63. <scope>import</scope>
  64. </dependency>
  65. </dependencies>
  66. </dependencyManagement>
  67. <build>
  68. <plugins>
  69. <plugin>
  70. <groupId>org.springframework.boot</groupId>
  71. <artifactId>spring-boot-maven-plugin</artifactId>
  72. </plugin>
  73. </plugins>
  74. </build>
  75. </project>

注意这里是SR1,经测试SR2有bug,会出问题。

  1. @Configuration
  2. public class RedisConfig {
  3. @Bean(name = {"redisTemplate", "stringRedisTemplate"})
  4. public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
  5. StringRedisTemplate redisTemplate = new StringRedisTemplate();
  6. redisTemplate.setConnectionFactory(factory);
  7. return redisTemplate;
  8. }
  9. }

核心类:

  1. @Component
  2. public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
  3. public static final String GATEWAY_ROUTES = "geteway_routes";
  4. @Resource
  5. private StringRedisTemplate redisTemplate;
  6. @Override
  7. public Flux<RouteDefinition> getRouteDefinitions() {
  8. List<RouteDefinition> routeDefinitions = new ArrayList<>();
  9. redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
  10. .forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class)));
  11. return Flux.fromIterable(routeDefinitions);
  12. }
  13. @Override
  14. public Mono<Void> save(Mono<RouteDefinition> route) {
  15. return null;
  16. }
  17. @Override
  18. public Mono<Void> delete(Mono<String> routeId) {
  19. return null;
  20. }
  21. }

主要是在get方法里,此处从redis里获取配置好的Definition。

然后我们的工作就是将配置信息,放到redis里即可。

下面就是我模拟的一个配置,等同于在yml里

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: header
  6. uri: http://localhost:8888/header
  7. filters:
  8. - AddRequestHeader=header, addHeader
  9. - AddRequestParameter=param, addParam
  10. predicates:
  11. - Path=/jd
  1. @Resource
  2. private StringRedisTemplate redisTemplate;
  3. @PostConstruct
  4. public void main() {
  5. RouteDefinition definition = new RouteDefinition();
  6. definition.setId("id");
  7. URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
  8. // URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
  9. definition.setUri(uri);
  10. //定义第一个断言
  11. PredicateDefinition predicate = new PredicateDefinition();
  12. predicate.setName("Path");
  13. Map<String, String> predicateParams = new HashMap<>(8);
  14. predicateParams.put("pattern", "/jd");
  15. predicate.setArgs(predicateParams);
  16. //定义Filter
  17. FilterDefinition filter = new FilterDefinition();
  18. filter.setName("AddRequestHeader");
  19. Map<String, String> filterParams = new HashMap<>(8);
  20. //该_genkey_前缀是固定的,见org.springframework.cloud.gateway.support.NameUtils类
  21. filterParams.put("_genkey_0", "header");
  22. filterParams.put("_genkey_1", "addHeader");
  23. filter.setArgs(filterParams);
  24. FilterDefinition filter1 = new FilterDefinition();
  25. filter1.setName("AddRequestParameter");
  26. Map<String, String> filter1Params = new HashMap<>(8);
  27. filter1Params.put("_genkey_0", "param");
  28. filter1Params.put("_genkey_1", "addParam");
  29. filter1.setArgs(filter1Params);
  30. definition.setFilters(Arrays.asList(filter, filter1));
  31. definition.setPredicates(Arrays.asList(predicate));
  32. System.out.println("definition:" + JSON.toJSONString(definition));
  33. redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
  34. }

定义好后,将其放到redis里,之后启动项目访问/jd,再启动后台的localhost:8888项目。即可进行验证。

之后如果要动态修改配置,就可以通过类似于上面的方式,来获取json字符串,然后将字符串放到redis里进行替换。替换后,需要通知gateway主动刷新一下。

刷新时,可以定义一个controller,然后调用一下notifyChanged()方法,就能完成新配置的替换了。

3 通过REST接口

gateway是自带接口能增删改查配置的,这个网上有比较多的教程,随便找个看看就明白了。譬如:

http://springcloud.cn/view/368

我发个类作为参考

  1. package com.maimeng.apigateway.route;
  2. import com.alibaba.fastjson.JSON;
  3. import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
  4. import org.springframework.cloud.gateway.filter.FilterDefinition;
  5. import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
  6. import org.springframework.cloud.gateway.route.RouteDefinition;
  7. import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
  8. import org.springframework.context.ApplicationEventPublisher;
  9. import org.springframework.context.ApplicationEventPublisherAware;
  10. import org.springframework.data.redis.core.StringRedisTemplate;
  11. import org.springframework.stereotype.Service;
  12. import org.springframework.web.util.UriComponentsBuilder;
  13. import reactor.core.publisher.Mono;
  14. import javax.annotation.PostConstruct;
  15. import javax.annotation.Resource;
  16. import java.net.URI;
  17. import java.util.Arrays;
  18. import java.util.HashMap;
  19. import java.util.Map;
  20. import static com.maimeng.apigateway.repository.RedisRouteDefinitionRepository.GATEWAY_ROUTES;
  21. /**
  22. * @author wuweifeng wrote on 2018/10/25.
  23. */
  24. @Service
  25. public class DynamicRouteService implements ApplicationEventPublisherAware {
  26. @Resource
  27. private RouteDefinitionWriter routeDefinitionWriter;
  28. private ApplicationEventPublisher publisher;
  29. private void notifyChanged() {
  30. this.publisher.publishEvent(new RefreshRoutesEvent(this));
  31. }
  32. /**
  33. * 增加路由
  34. *
  35. */
  36. public String add(RouteDefinition definition) {
  37. routeDefinitionWriter.save(Mono.just(definition)).subscribe();
  38. notifyChanged();
  39. return "success";
  40. }
  41. /**
  42. * 更新路由
  43. */
  44. public String update(RouteDefinition definition) {
  45. try {
  46. this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
  47. } catch (Exception e) {
  48. return "update fail,not find route routeId: " + definition.getId();
  49. }
  50. try {
  51. routeDefinitionWriter.save(Mono.just(definition)).subscribe();
  52. notifyChanged();
  53. return "success";
  54. } catch (Exception e) {
  55. return "update route fail";
  56. }
  57. }
  58. /**
  59. * 删除路由
  60. *
  61. */
  62. public String delete(String id) {
  63. try {
  64. this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
  65. notifyChanged();
  66. return "delete success";
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. return "delete fail";
  70. }
  71. }
  72. @Override
  73. public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
  74. this.publisher = applicationEventPublisher;
  75. }
  76. @Resource
  77. private StringRedisTemplate redisTemplate;
  78. @PostConstruct
  79. public void main() {
  80. RouteDefinition definition = new RouteDefinition();
  81. definition.setId("id");
  82. URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
  83. // URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
  84. definition.setUri(uri);
  85. //定义第一个断言
  86. PredicateDefinition predicate = new PredicateDefinition();
  87. predicate.setName("Path");
  88. Map<String, String> predicateParams = new HashMap<>(8);
  89. predicateParams.put("pattern", "/jd");
  90. predicate.setArgs(predicateParams);
  91. //定义Filter
  92. FilterDefinition filter = new FilterDefinition();
  93. filter.setName("AddRequestHeader");
  94. Map<String, String> filterParams = new HashMap<>(8);
  95. //该_genkey_前缀是固定的,见org.springframework.cloud.gateway.support.NameUtils类
  96. filterParams.put("_genkey_0", "header");
  97. filterParams.put("_genkey_1", "addHeader");
  98. filter.setArgs(filterParams);
  99. FilterDefinition filter1 = new FilterDefinition();
  100. filter1.setName("AddRequestParameter");
  101. Map<String, String> filter1Params = new HashMap<>(8);
  102. filter1Params.put("_genkey_0", "param");
  103. filter1Params.put("_genkey_1", "addParam");
  104. filter1.setArgs(filter1Params);
  105. definition.setFilters(Arrays.asList(filter, filter1));
  106. definition.setPredicates(Arrays.asList(predicate));
  107. System.out.println("definition:" + JSON.toJSONString(definition));
  108. redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
  109. }
  110. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/317425?site
推荐阅读
相关标签
  

闽ICP备14008679号