Spring Cloud Gateway 入門

aoho發表於2018-03-11

Spring Cloud Gateway 介紹

前段時間剛剛釋出了Spring Boot 2正式版,Spring Cloud Gateway基於Spring Boot 2,是Spring Cloud的全新專案,該專案提供了一個構建在Spring 生態之上的API閘道器,包括:Spring 5,Spring Boot 2和Project Reactor。 Spring Cloud Gateway旨在提供一種簡單而有效的途徑來傳送API,併為他們提供橫切關注點,例如:安全性,監控/指標和彈性。當前最新的版本是v2.0.0.M8,正式版最近也會到來。

Spring Cloud Gateway的特徵:

  • Java 8
  • Spring Framework 5
  • Spring Boot 2
  • 動態路由
  • 內建到Spring Handler對映中的路由匹配
  • 基於HTTP請求的路由匹配 (Path, Method, Header, Host, etc…​)
  • 過濾器作用於匹配的路由
  • 過濾器可以修改下游HTTP請求和HTTP響應 (Add/Remove Headers, Add/Remove Parameters, Rewrite Path, Set Path, Hystrix, etc…​)
  • 通過API或配置驅動
  • 支援Spring Cloud DiscoveryClient配置路由,與服務發現與註冊配合使用

vs Netflix Zuul

Zuul基於servlet 2.5(使用3.x),使用阻塞API。 它不支援任何長連線,如websockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 Websockets得到支援,並且由於它與Spring緊密整合,所以將會是一個更好的開發體驗。

Spring Cloud Gateway入門實踐

筆者最近研讀了Spring Cloud Gateway的原始碼,大部分功能的實現也寫了原始碼分析的文章,但畢竟正式版沒有釋出,本文算是一篇入門實踐,展示常用的幾個功能,期待最近的正式版本釋出。

示例啟動兩個服務:Gateway-Server和user-Server。模擬的場景是,客戶端請求後端服務,閘道器提供後端服務的統一入口。後端的服務都註冊到服務發現Consul(搭建zk,Eureka都可以,筆者比較習慣使用consul)。閘道器通過負載均衡轉發到具體的後端服務。

使用者服務

使用者服務註冊到Consul上,並提供一個介面/test

依賴

需要的依賴如下:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
複製程式碼

配置檔案

spring:
  application:
    name: user-service
  cloud:
    consul:
      host: 192.168.1.204
      port: 8500
      discovery:
        ip-address: ${HOST_ADDRESS:localhost}
        port: ${SERVER_PORT:${server.port}}
        healthCheckPath: /health
        healthCheckInterval: 15s
        instance-id: user-${server.port}
        service-name: user
server:
  port: 8005
management:
  security:
    enabled: false
複製程式碼

暴露介面

@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class GatewayUserApplication {

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

    @GetMapping("/test")
    public String test() {
        return "ok";
    }
}
複製程式碼

暴露/test介面,返回ok即可。

閘道器服務

閘道器服務提供多種路由配置、路由斷言工廠和過濾器工廠等功能。

依賴

需要引入的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator</artifactId>
</dependency>
//依賴於webflux,必須引入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gateway-core</artifactId>
</dependency>
//服務發現元件,排除web依賴
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    <version>2.0.0.M6</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
    </exclusions>
</dependency>
//kotlin依賴
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
    <version>${kotlin.version}</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
    <version>${kotlin.version}</version>
    <optional>true</optional>
</dependency>
複製程式碼

如上引入了kotlin相關的依賴,這裡需要支援kotlin的路由配置。Spring Cloud Gateway的使用需要排除web相關的配置,引入的是webflux的引用,應用啟動時會檢查,必須引入。

路由斷言工廠

路由斷言工廠有多種型別,根據請求的時間、host、路徑、方法等等。如下定義的是一個基於路徑的路由斷言匹配。

    @Bean
    public RouterFunction<ServerResponse> testFunRouterFunction() {
        RouterFunction<ServerResponse> route = RouterFunctions.route(
                RequestPredicates.path("/testfun"),
                request -> ServerResponse.ok().body(BodyInserters.fromObject("hello")));
        return route;
    }
複製程式碼

當請求的路徑為/testfun時,直接返回ok的狀態碼,且響應體為hello字串。

過濾器工廠

閘道器經常需要對路由請求進行過濾,進行一些操作,如鑑權之後構造頭部之類的,過濾的種類很多,如增加請求頭、增加請求引數、增加響應頭和斷路器等等功能。

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
        //@formatter:off
        return builder.routes()
                .route(r -> r.path("/image/webp")
                        .filters(f ->
                                f.addResponseHeader("X-AnotherHeader", "baz"))
                        .uri("http://httpbin.org:80")
                )
                .build();
        //@formatter:on
    }
複製程式碼

如上實現了當請求路徑為/image/webp時,將請求轉發到http://httpbin.org:80,並對響應進行過濾處理,增加響應的頭部X-AnotherHeader: baz

自定義路由

上面兩小節屬於API自定義路由,還可以通過配置進行定義:

spring:
  cloud:
    gateway:
      locator:
        enabled: true
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar

      routes:
      # =====================================
      - id: default_path_to_http
        uri: blueskykong.com
        order: 10000
        predicates:
        - Path=/**
複製程式碼

如上的配置定義了路由與過濾器。全域性過濾器將所有的響應加上頭部X-Response-Default-Foo: Default-Bar。定義了id為default_path_to_http的路由,只是優先順序比較低,當該請求都不能匹配時,將會轉發到blueskykong.com

kotlin自定義路由

Spring Cloud Gateway可以使用kotlin自定義路由:

@Configuration
class AdditionalRoutes {

	@Bean
	fun additionalRouteLocator(builder: RouteLocatorBuilder): RouteLocator = builder.routes {
		route(id = "test-kotlin") {
			path("/image/png")
			filters {
				addResponseHeader("X-TestHeader", "foobar")
			}
			uri("http://httpbin.org:80")
		}
	}

}
複製程式碼

當請求的路徑是/image/png,將會轉發到http://httpbin.org:80,並設定了過濾器,在其響應頭中加上了X-TestHeader: foobar頭部。

服務發現元件

與服務註冊於發現元件進行結合,通過serviceId轉發到具體的服務例項。在前面的配置已經引入了相應的依賴。

    @Bean
    public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
        return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
    }
複製程式碼

DiscoveryClient注入到DiscoveryClientRouteDefinitionLocator的建構函式中,關於該路由定義定位器,後面的原始碼分析會講解,此處不展開。

spring:
  cloud:
    gateway:
      locator:
        enabled: true
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      routes:
      # =====================================
      - id: service_to_user
        uri: lb://user
        order: 8000
        predicates:
        - Path=/user/**
        filters:
        - StripPrefix=1
複製程式碼

上面的配置開啟了DiscoveryClient定位器的實現。路由定義了,所有請求路徑以/user開頭的請求,都將會轉發到user服務,並應用路徑的過濾器,擷取掉路徑的第一部分字首。即訪問/user/test的實際請求轉換成了lb://user/test

websocket

還可以配置websocket的閘道器路由:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar

      routes:
      - id: websocket_test
        uri: ws://localhost:9000
        order: 9000
        predicates:
        - Path=/echo
複製程式碼

啟動一個ws服務端wscat --listen 9000,將閘道器啟動(閘道器埠為9090),進行客戶端連線即可wscat --connect ws://localhost:9090/echo

客戶端的訪問

上述實現的功能,讀者可以自行下載原始碼進行嘗試。筆者這裡只展示訪問使用者服務的結果:

Spring Cloud Gateway 入門
Spring Cloud Gateway 入門

閘道器成功負載均衡到user-server,並返回了ok。響應的頭部中包含了全域性過濾器設定的頭部X-Response-Default-Foo: Default-Bar

總結

在本文中,我們探討了屬於Spring Cloud Gateway的一些功能和元件。 這個新的API提供了用於閘道器和代理支援的開箱即用工具。期待Spring Cloud Gateway 2.0正式版。

原始碼地址

github.com/keets2012/S…

訂閱最新文章,歡迎關注我的公眾號

微信公眾號

相關文章