網關我選 Spring Cloud Gateway

網關可提供請求路由與組合協議轉換安全認證服務鑑權流量控制日誌監控等服務。可選的網關有很多,好比 Nginx、高性能網關 OpenResty、Linkerd 以及 Spring Cloud Gateway。java

若是是真的追求高性能,那確定是選擇 Nginx 或者 OpenResty 無疑了, 可是對性能要求不是很高的話,而且又在用 Spring Cloud 系列,那固然就要選擇 Spring Cloud Gateway 了。git

網關的基礎就是路由功能,通俗解釋就是地址轉發,將一個請求地址轉發到實際的服務地址。好比請求的是 http://xxx.com/api 的路由地址,實際上會被轉發到 http://xxx.com:8888 上來,這就是個最簡單的路由方式。github

咱們能夠理解爲 Spring Cloud Gateway 就是針對進來的請求作各類判斷和處理,好比說判斷請求的合法性、權限驗證,請求地址改寫,請求參數、頭信息、cookie 信息的分析和改寫,請求速率控制,日誌留存等。而這些均可以方便的經過 Predicate 和 GatewayFilter 來組合實現。web

建立 Spring Cloud Gateway 項目

Spring Cloud 版本是 Greenwich.SR2,Spring Boot 版本 2.1.6.RELEASE,JDK 1.8。spring

接下來正式建立一個 Gateway 項目。編程

首先作兩個微服務,當作路由轉發的目標服務api

兩個微服務是以 consul 做爲服務註冊中心的,能夠看這篇文章服務註冊發現、配置中心集一體的 Spring Cloud Consul安全

一、建立 consul-order 服務,具體能夠去 github 上看代碼(https://github.com/huzhicheng/spring-cloud-study/tree/master/consul/consul-order),很簡單的一個服務。建立的 RESTful Controller 以下:cookie

@RestController
@RequestMapping(value = "order")
public class OrderController {

    @Value("${spring.application.name}")
    private String applicationName;


    @GetMapping(value = "get")
    public CustomerOrder getOrder(){
        CustomerOrder customerOrder = new CustomerOrder();
        customerOrder.setOrderId("9999");
        customerOrder.setProductName("MacBook Pro");
        customerOrder.setClient(applicationName);
        return customerOrder;
    }
}

總之,最後直接訪問這個接口的地址爲 http://localhost:5006/order/getapp

二、建立 consul-user 服務,具體代碼能夠到 github 上查看(https://github.com/huzhicheng/spring-cloud-study/tree/master/consul/consul-user)。建立的 RESTful Controller 內容以下:

@RestController
@RequestMapping(value = "user")
public class UserController {

    @GetMapping(value = "get")
    public User getUserInfo(){
        User user = new User();
        user.setName("古時的風箏");
        user.setAge(8);
        user.setLocation("北京");
        return user;
    }
}

和上面的微服務有點區別的就是設置了 context-path

server:
  port: 5005
  servlet:
    context-path: /user-service

之因此這樣不一樣的設置,是由於下面要驗證一個 filter。總之,最後上述接口的訪問地址爲:http://localhost:5005/user-service/user/get

建立一個項目,並引入 maven 包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置簡單的路由轉發

路由配置有兩種方式。一種是配置文件,另一種是代碼方式配置,WebFlux 的反應式編程方式。因此咱們 pom 文件中要引入 WebFlux 的包。這是 Spring 5 的新特性。

一、先看第一種配置文件方式配置:

server:
  port: 10000
spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: false # 是否將服務id轉換爲小寫
      routes:
      - id: userServiceRouter
        uri: lb://consul-user
        predicates:
        - Path=/user-service/**
      - id: orderServiceRouter
        uri: lb://consul-order
        predicates:
        - Path=/order-service/**
        filters:
        - StripPrefix=1
    consul:
      host: localhost #註冊gateway網關到consul
      port: 8500
      discovery:
        service-name: service-gateway

其中包括 Spring Boot 項目的基本配置,name、port ,還有關於 consul 的配置,要將網關服務註冊到註冊中心。

上面配置中建立了兩條路由規則,路由規則名稱經過 id 設置,分別是 userServiceRouter 和 orderServiceRouter,經過 predicates.Path 設置待轉發的 url,經過 uri 設置轉發後的目標地址。上面配置將以/user-service/開頭的地址轉發到 lb://consul-user,固定格式 lb + 服務id,在有註冊中心的狀況下要這樣寫,如過沒有註冊中心,能夠直接寫目標 url。

下面的路由規則中多了一個 StripPrefix 的 filter ,這個是 Gateway 的內置 filter,做用就是去掉 Path 中的指定部分,StripPrefix=1,就是以 / 分隔,去掉第一部分,好比 /a/b/c 這個地址,在 StripPrefix=1 的做用下,就會轉發到 /b/c/,當 StripPrefix=2 的時候,就會轉發到 /c/。

配置好上述接口,而後啓動網關服務。訪問規則就會有以下對應關係:

http://localhost:10000/user-service/user/get->http://localhost:5005/user-service/user/get

http://localhost:10000/order-service/order/get->http://localhost:5006/order/get

固然這只是針對每個目標服務只有一個實例的狀況,若是有多個實例,就會按照負載策略落到對應的實例中。

二、代碼方式的路由配置

@Bean
public RouteLocator kiteRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("userRouter", r -> r.path("/user-service/**")
            .filters(f ->
                f.addResponseHeader("X-CustomerHeader", "kite"))
            .uri("lb://consul-user")
        )
        .route("orderRouter", r -> r.path("/order-service/**")
            .filters(f -> f.stripPrefix(1)).uri("lb://consul-order")
        )
        .build();
}

上面的這段代碼和前面的配置文件的內容是一樣的做用。只要實現一個返回類型爲 RouteLocator,參數爲 RouteLocatorBuilder類型的 Bean。

你看後面那一連串的 r.path().filters().uri() 了嗎,用它們就能夠簡單的配置出路由規則,並且可讀性也比較強。另外,Gateway 還套用了 Predicate 的規則來構建更加靈活、複雜的路由規則。Predicate 是 Java 8 增長的邏輯計算庫,有 negate()、and()、or()、isEqual()幾個方法。具體的代碼在 PredicateSpec 和 UriSpec 這兩個類裏,一目瞭然。

PredicateSpec 裏有這麼多方法,均可以結合 and、or 組合起來使用。

接下來就說到 filter,Gateway 內置了不少的 filter,能夠在 GatewayFilterSpec 類下找到方法封裝,每個 filter 都由一個 factory 的 apply 實現,都在 org.springframework.cloud.gateway.filter.factory包下,有必要的話能夠直接看源碼。 好比上面用到的 StripPrefix。還有 addResponseHeader,它的做用是在 Response 對象的 header 中添加請求頭。

啓動網關服務

啓動網關,並訪問兩個接口測試,接口分別爲 http://localhost:10000/user-service/user/get和http://localhost:10000/order-service/order/get,正常返回數據,則說明網關服務配置正常。

巧用 StripPrefix filter

微服務多了以後,路由的轉發規則也就多了,比方說訂單相關請求要轉發到訂單微服務集羣,用戶相關請求要轉發到用戶微服務集羣,最終開放給終端的接口也要能代表是哪一個微服務的,除了接口文檔裏說明以外,接口自己最好也能明確標識。

一種方式是在微服務的配置文件中配置上server.servlet.context-path

還有一種方式就是在路由規則的 path 中配置,而後加上 StripPrefix 配置,選擇性的去掉請求 url 中的某些部分。好比咱們請求 Gateway的地址爲 order-service/order/get,則通過 StripPrefix(1) 以後,會把請求地址變爲 order/get,而後根據路由規則定向到具體的微服務地址或者特定的 url。

本篇就介紹 Spring Cloud Gateway 的基本用法,後續還會有關於集成安全認證、鑑權、限流、日誌等相關內容,敬請關注。

不要吝惜你的「推薦」呦

歡迎關注,不按期更新本系列和其餘文章
古時的風箏 ,進入公衆號能夠加入交流羣

相關文章
相關標籤/搜索