spring-cloud-gateway 快速入门

简介

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

特征

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0

  • 动态路由

  • Predicates 和 Filters 作用于特定路由

  • 集成 Hystrix 断路器

  • 集成 Spring Cloud DiscoveryClient

  • 易于编写的 Predicates 和 Filters

  • 限流

  • 路径重写

1.demo编写,依赖于springboot添加依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Greenwich.SR2</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
        <version>2.1.2.RELEASE</version>
    </dependency>
</dependencies>

编写启动类:

@SpringBootApplication
public class Application {

   public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
   }

   @Bean
   public RouteLocator myRoutes(RouteLocatorBuilder builder) {
      return builder.routes()
            .route(p -> p
                    //path可以采用表达式
                  .path("/api/{segment}", "/api1/{segment}")
                  .filters(f -> f.addRequestHeader("user", "dandan"))
                  .uri("http://localhost:8088")
            )
            .build();
   }
}

这里采用bean方式将需要代理的router配置进去,访问gateway接口 http://localhost:8080/api/hello,则转向目标服务:http://localhost:8088/api/hello

2.深入测试predicate

(1)before, after, between 配置

before: 在设定的时间之前能够访问router

after: 在设定的时间之后能够访问router

between: 在时间区域之间能够访问router

PS: 时间设定值需要包含时区

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - After=2021-03-21T17:42:47.789-07:00[Asia/Shanghai]
        - Before=2021-03-26T17:42:47.789-07:00[Asia/Shanghai]
        - Between=2021-03-21T17:42:47.789-07:00[Asia/Shanghai], 2021-03-26T17:42:47.789-07:00[Asia/Shanghai]

(2)添加cookie:

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - Cookie=chocolate, ch.p

(3) 添加header,并且header支持正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - Header=X-Request-Id, \d+

(4)配置method, 但是method值只能是post或get,不能两个都写上

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - Method=GET

(5)path匹配: - Path=/red/{segment},/blue/{segment},path的值可以配置多个路径

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}

(6)参数匹配,当配置上query时,请求必须带上参数才能访问该route: - Query=red

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - Query=red

(7)权重设置:

spring:
  cloud:
    gateway:
      routes:
      - id: hello_route0
        uri: http://localhost:8088/api/hello
        predicates:
        - Path=/api/{segment},/api1/{segment}
        - Weight=group1,8

3.spring-cloud-gateway-mvc模块代码解析主要功能,依托于spring-MVC,

将请求信息封装到proxyExchange中,通过proxyExchange。

forward方法跳转到真是方法上比较重要的类

1.配置类ProxyResponseAutoConfiguration:

/**
*在web项目项目启动时,加载该类
*/

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@ConditionalOnClass({ HandlerMethodReturnValueHandler.class })
@EnableConfigurationProperties(ProxyProperties.class)
public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {

   @Autowired
   private ApplicationContext context;

    //将配置信息分装到ProxyProperties类型proxy的对象中
    //properties = { "spring.cloud.gateway.proxy.auto-forward=Baz,Baz1" }
   @Bean
   @ConditionalOnMissingBean
   public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(Optional<RestTemplateBuilder> optional,
         ProxyProperties proxy) {
      RestTemplateBuilder builder = optional.orElse(new RestTemplateBuilder());
      RestTemplate template = builder.build();
      template.setErrorHandler(new NoOpResponseErrorHandler());
      template.getMessageConverters().add(new ByteArrayHttpMessageConverter() {
         @Override
         public boolean supports(Class<?> clazz) {
            return true;
         }
      });
      //初始化resolver
      ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(template);
      resolver.setHeaders(proxy.convertHeaders());
      resolver.setAutoForwardedHeaders(proxy.getAutoForward());
      resolver.setSensitive(proxy.getSensitive()); // can be null
      return resolver;
   }

   @Override
   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      argumentResolvers.add(context.getBean(ProxyExchangeArgumentResolver.class));
   }

   private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {

      @Override
      public void handleError(ClientHttpResponse response) throws IOException {
      }
   }
}

(2)ProxyExchangeArgumentResolver中有一个非常重要的方法resolveArgument

通过实现HandlerMethodArgumentResolver接口,来实现resolveArgument方法,这个方法主要是返回ProxyExchange对象

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
   ProxyExchange<?> proxy = new ProxyExchange<>(rest, webRequest, mavContainer, binderFactory, type(parameter));
   proxy.headers(headers);
   if (this.autoForwardedHeaders.size() > 0) {
      proxy.headers(extractAutoForwardedHeaders(webRequest));
   }
   if (sensitive != null) {
      proxy.sensitive(sensitive.toArray(new String[0]));
   }
   return proxy;

该方法在InvocableHandlerMethod类中的invokeForRequest方法中会用到,需要用到resolve返回的proxy, 进行方法匹配,跳转到目标路径:

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
   //该方法获取到的就是proxyExchange对象
   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
   }
   //将proxyExchange对象传入到doInvoke中,进行真实跳转
   return doInvoke(args);
}

跳转到测试方法之后,通过proxy去路由到真实的服务:

@GetMapping("/forward/**")
public void forward(ProxyExchange<?> proxy) throws Exception {
   String path = proxy.path("/forward");
   if (path.startsWith("/special")) {
      proxy.header("X-Custom", "FOO");
      path = proxy.path("/forward/special");
   }
   proxy.forward(path);
}

真实的路由跳转目标方法是依赖于spring的mapping:

public void forward(String path) {
   HttpServletRequest request = this.webRequest.getNativeRequest(HttpServletRequest.class);
   HttpServletResponse response = this.webRequest.getNativeResponse(HttpServletResponse.class);
   try {
      request.getRequestDispatcher(path).forward(new BodyForwardingHttpServletRequest(request, response),
            response);
   }
   catch (Exception e) {
      throw new IllegalStateException("Cannot forward request", e);
   }
Comments: 0

「人生在世,留句话给我吧」

提交评论