zoukankan      html  css  js  c++  java
  • Spring Cloud Gateway简单使用

    一、引子

    2年前有幸使用过一次Spring Cloud (1.5.9),1.* 集成的是ZUUL做网关。终于在2年后,这次果断使用Spring Cloud Gateway。

    区别:

    Zuul构建于 Servlet 2.5,兼容 3.x,使用的是阻塞式的 API,不支持长连接,比如 websockets。

    Spring Cloud Gateway构建于 Spring 5+,基于 Spring Boot 2.x 响应式的、非阻塞式的 API。支持 websockets,和Spring 框架紧密集成。底层使用netty模型,性能极高。

    备注
    spring cloud 已放弃zuul 2.0,使用了自己的亲儿子gateway.后续估计也不会再集成2.0了,建议从zuul转向gateway.

    一个简单的创业项目架构图如下:

    二、Gateway设计思想

    2.1 官网设计

    自从撇开netflex zuul后,spring Cloud速度搜搜的。我开发时还是用2.1.4,目前最新已经到2.2.1,附上官网飞机票

    2.1.1 特性

    • Built on Spring Framework 5, Project Reactor and Spring Boot 2.0:基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 

    • Able to match routes on any request attribute.:能匹配任意请求属性的路由

    • Predicates and filters are specific to routes.:针对特定路由使用匹配策略和过滤器

    • Hystrix Circuit Breaker integration. :集成Hystri断路器

    • Spring Cloud DiscoveryClient integration:集成服务发现(gateway一样可注册到eureka)

    • Easy to write Predicates and Filters:易于写策略(断言)+过滤器

    • Request Rate Limiting 请求限流

    • Path Rewriting:重写path

    简单来说就是Route、Predicate、Filter三大核心组件。

    2.1.2 流程图

    如上图,Gateway Client客户端发送请求在Gateway Handler Mapping中查找是否命中路由策略,命中的话请求转发给Gateway Web Handler来处理。根据定义的多个Filter链,执行顺序:Pre Filter->代理请求->Post Filter。

    2.1.3 内置Predicates+Filter

    Gateway内置了11个Predicates Factories路由策略(断言)工厂类。

    Filter分2类:

    • 31个GatewayFilter Factories网关过滤器工厂类
    • 10个GlobalFilter 全局过滤器接口

    这里就不在过多介绍,建议有需求时可以去官网找找,没有的话再自己开发。

    2.2 我们的使用

    1.使用Route结合Hystrix实现默认降级策略

    2.使用GatewayFilter接口,自定义过滤器类,实现登录态(token)校验

    三、Gateway简单使用

    3.1 实现微服务的默认降级策略

    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: false
              #开启小写验证,默认feign根据服务名查找都是用的全大写
              lowerCaseServiceId: true
          default-filters:
            - AddResponseHeader=X-Response-Default-Foo, Default-Bar
          routes:
            - id: OLOAN-FINANCIAL-PRODUCT-SERVICE
              # lb代表从注册中心获取服务
              uri: lb://OLOAN-FINANCIAL-PRODUCT-SERVICE
              predicates:
                # 转发该路径
                - Path=/gateway/financialProduct/**
              # 带前缀
              filters:
                - StripPrefix=1
                - name: Hystrix
                  args:
                    name: fallbackcmd
                    fallbackUri: forward:/defaultfallback
            - id: ADMIN-SERVICE
              uri: lb://ADMIN-SERVICE
              predicates:
                - Path=/gateway/auth/**
              filters:
                - StripPrefix=2
                - name: Hystrix
                  args:
                    name: fallbackcmd
                    fallbackUri: forward:/defaultfallback
    

     如上图,我们开启了2个微服务route路由。

    • 1)前端请求时path带/gateway/,在gateway层使用StripPrefix=1,去掉gateway,最终微服务上的path不带"/gateway/".
    • 2)使用Hystrix实现默认降级策略,降级接口实现如下:
    @Slf4j
    @RestController
    public class DefaultHystrixController {
    
        @RequestMapping("/defaultfallback")
        public ApiResult defaultfallback(){
    
            log.info("服务降级中");
            return ApiResult.failure("服务异常");
        }
    }
    

     3.2 实现登录态(token)校验

    3.2.1 自定义过滤器

    自定义过滤器,实现GatewayFilter, Ordered 2个接口。

     1 import com.*.auth.UserTokenTools;
     2 import lombok.extern.slf4j.Slf4j;
     3 import org.apache.commons.lang3.StringUtils;
     4 import org.springframework.cloud.gateway.filter.GatewayFilter;
     5 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
     6 import org.springframework.core.Ordered;
     7 import org.springframework.http.HttpHeaders;
     8 import org.springframework.http.HttpStatus;
     9 import org.springframework.http.server.reactive.ServerHttpRequest;
    10 import org.springframework.http.server.reactive.ServerHttpResponse;
    11 import org.springframework.stereotype.Component;
    12 import org.springframework.web.server.ServerWebExchange;
    13 import reactor.core.publisher.Mono;
    14 
    15 /**
    16  * @author denny.zhang
    17  * @Description token过滤器
    18  * @date 2019/12/12 13:55
    19  */
    20 @Slf4j
    21 @Component
    22 public class LoginTokenFilter implements GatewayFilter, Ordered {
    23 
    24     private static final String AUTHORIZE_TOKEN = "Authorization";
    25     private static final String BEARER = "Bearer ";
    26 
    27     /**
    28      * token过滤
    29      *
    30      * @param exchange
    31      * @param chain
    32      * @return
    33      */
    34     @Override
    35     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    36         log.info("当前环境已开启token校验");
    37         ServerHttpRequest request = exchange.getRequest();
    38         HttpHeaders headers = request.getHeaders();
    39         ServerHttpResponse response = exchange.getResponse();
    40         // 取Authorization
    41         String tokenHeader = headers.getFirst(AUTHORIZE_TOKEN);
    42         log.info("tokenHeader=" + tokenHeader);
    43         // token不存在
    44         if (StringUtils.isEmpty(tokenHeader)) {
    45             response.setStatusCode(HttpStatus.UNAUTHORIZED);
    46             return response.setComplete();
    47         }
    48         // 取token
    49         String token = this.getToken(tokenHeader);
    50         log.info("token=" + token);
    51 
    52         // token不存在
    53         if (StringUtils.isEmpty(token)) {
    54             log.info("token不存在");
    55             response.setStatusCode(HttpStatus.UNAUTHORIZED);
    56             return response.setComplete();
    57         }
    58         // 校验 token是否失效
    59         if (UserTokenTools.isTokenExpired(token, null)) {
    60             log.info("token失效");
    61             response.setStatusCode(HttpStatus.UNAUTHORIZED);
    62             return response.setComplete();
    63         }
    64         // 校验 token是否正确
    65         if (!UserTokenTools.checkToken(token, null)) {
    66             response.setStatusCode(HttpStatus.UNAUTHORIZED);
    67             return response.setComplete();
    68         }
    69 
    70 //        //有token 这里可根据具体情况,看是否需要在gateway直接把解析出来的用户信息塞进请求中,我们最终没有使用
    71 //        UserTokenInfo userTokenInfo = UserTokenTools.getUserTokenInfo(token);
    72 //        log.info("token={},userTokenInfo={}",token,userTokenInfo);
    73 //        request.getQueryParams().add("token",token);
    74         //request.getHeaders().set("token", token);
    75         return chain.filter(exchange);
    76     }
    77 
    78 
    79     @Override
    80     public int getOrder() {
    81         return -10;
    82     }
    83 
    84     /**
    85      * 解析Token
    86      */
    87     public String getToken(String requestHeader) {
    88         //2.Cookie中没有从header中获取
    89         if (requestHeader != null && requestHeader.startsWith(BEARER)) {
    90             return requestHeader.substring(7);
    91         }
    92         return "";
    93     }
    94 }

    上图中,UserTokenTools是我们自定义的一个JWT工具类,用来生成token,校验token过期、正确等。

    3.2.2 配置路由

    大家可根据具体情况,如果只有一套登录态,那就用一个filter即可。

     1 import com.*.gateway.filter.AuthorizeGatewayFilter;
     2 import com.*.gateway.filter.LoginTokenFilter;
     3 import org.springframework.cloud.gateway.route.RouteLocator;
     4 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
     5 import org.springframework.context.annotation.Bean;
     6 import org.springframework.context.annotation.Configuration;
     7 
     8 
     9 @Configuration
    10 public class GatewayConfig {
    11 
    12     @Bean
    13     public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
    14         return builder.routes()
    15                 // token校验1
    16                 .route(predicateSpec -> predicateSpec
    17                         .path("/gateway/pay/card/**", "/gateway/app/**")
    18                         .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new AuthorizeGatewayFilter()))
    19                         .uri("lb://OLOAN-PAY-SERVICE")
    20                         .id("OLOAN-PAY-SERVICE-token"))
    21 
    22                 // token校验2
    23                 .route(predicateSpec -> predicateSpec
    24                         .path("/gateway/order-audit/**", "/gateway/order/**", "/gateway/order-payment/**")
    25                         .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new LoginTokenFilter()))
    26                         .uri("lb://OLOAN-ORDER-SERVICE")
    27                         .id("OLOAN-ORDER-ORDER-token"))
    28                 .build();
    29     }
    30 }

    四、总结

    4.1.WebFlux

    Spring Cloud Gateway使用WebFlux,和spring boot web包冲突,使用时一定记得pom中排除原来老WEB那一套(servlet)相关jar,否则会报错。

    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-gateway</artifactId>
    	<exclusions>
    		<exclusion>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc</artifactId>
    		</exclusion>
    	</exclusions>
    </dependency>
    

    4.2.Gateway Filter

    Gateway Filter 自带的源码支撑错误码response.setStatusCode(HttpStatus.UNAUTHORIZED);并不是那么的友好。错误码枚举使用的是spring自带框架的枚举类:

    org.springframework.http.HttpStatus:

    UNAUTHORIZED(401, "Unauthorized")

    这样请求返回的结构体和一般定义的JSON格式(code message data)不同。当然官方也是提供了解决方案。后续再去优化吧。

    4.3 限流

    gateway默认实现了几个简单的限流策略(依赖redis),后续可以使用一下。

  • 相关阅读:
    SCI写作经典替换词,瞬间高大上!(转)
    最佳化常用测试函数 Optimization Test functions
    算法复杂度速查表
    VS 代码行统计
    CPLEX IDE 菜单栏语言设置( 中文 英文 韩文 等多国语言 设置)
    如何从PDF文件中提取矢量图
    Matlab无法打开M文件的错误( Undefined function or method 'uiopen' for input arguments of type 'char)
    visual studio 资源视图 空白 解决方案
    MFC DialogBar 按钮灰色不响应
    嗨翻C语言笔记(二)
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/12132612.html
Copyright © 2011-2022 走看看