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
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),后續可以使用一下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM