spring cloud gateway閘道器(一)之閘道器路由

2023-06-07 18:00:43

1、gateway相關介紹

在微服務架構中,系統往往由多個微服務組成,而這些服務可能部署在不同機房、不同地區、不同域名下。這種情況下,使用者端(例如瀏覽器、手機、軟體工具等)想要直接請求這些服務,就需要知道它們具體的地址資訊,例如 IP 地址、埠號等。這種使用者端直接請求服務的方式存在很多的複雜問題。如:需要維護大量的服務地址、存在跨域請求的問題、每個微服務需要獨立認證等。所以我們可以通過 API 閘道器(gateway)來解決這些問題。所有使用者端請求都發生到閘道器,由閘道器統一轉發到不同的地址。閘道器還可以 處理一些非業務功能的邏輯,例如許可權驗證、監控、快取等。本篇主要講解通過閘道器統一請求轉發到不同微服務。

2、建立閘道器服務

首先建立maven專案新建閘道器模組:

pom匯入閘道器相關依賴:

 <dependencies>
        <!--Eureka Client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--特別注意:在 gateway 閘道器服務中不能引入 spring-boot-starter-web 的依賴,否則會報錯-->
        <!-- Spring cloud gateway 閘道器依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- LoadBalancer -->
<!-- 引入spring-cloud-loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--ribbon Spring Cloud Ribbon 在高版本移除了 -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
<!-- <version>2.2.5.RELEASE</version>-->
<!-- </dependency>-->
    </dependencies>

建立springboot啟動類:

@SpringBootApplication
@EnableEurekaClient
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class,args);
    }

}

啟動類建立成功後,在resources資源目錄下建立application.yml檔案,用來設定gateway。首先設定伺服器埠和服務名稱:

server:
  port: 8081
spring:
  application:
    name: gateway-server-one

在把閘道器註冊到註冊中心:

#使用者端註冊
eureka:
  instance:
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port} #localhost
    hostname: localhost
  client:
    register-with-eureka: true #是否向註冊中心註冊自己 預設為true
    fetch-registry: true  #是否需要檢索服務  預設為true
    service-url:
      defaultZone: http://eternity:1234qwer@localhost:4405/eureka/

設定好之後,可以啟動閘道器。檢視閘道器服務是否可以正常啟動以及是否在註冊中心註冊成功,如果註冊成功然後在開始設定閘道器引數。

可以看到服務啟動成功,我們看看註冊中心是否成功:

eureka中心已經有了,說明啟動成功。

3、設定gateway路由

spring:
  application:
    name: gateway-server-one
  cloud:
    loadbalancer:
      ribbon:
        enabled: false #使用LoadBalancer, Ribbon 在高版本移除了 切換到ReactiveLoadBalancerClientFilter
    gateway:
      discovery: 
        locator: #路由存取方式:http://Gateway_HOST:Gateway_PORT/大寫的serviceId/**,其中微服務應用名預設大寫存取。
          enabled: true #開啟通過名稱找到伺服器功能
          lower-case-service-id: true #使用小寫service-id
      routes:
        - id: hibernate-server-one-route #路由的ID,沒有固定規則,但要求唯一,建議與服務名對應
          uri: http://localhost:8800  #匹配後提供服務的路由地址
          predicates:
            #以下是斷言條件,必選全部符合條件
            - Path=/admin/user/**   #斷言,路徑相匹配的進行路由
            - Method=GET #只能時 GET 請求時,才能存取
        - id: xxl-job-server-one-route
#          uri: http://localhost:8801 #可以設定為服務名
          uri: lb://XXL-JOB-SERVER-MODEL #可以設定為服務名
          predicates:
            - Path=/admin/user/**   #斷言,路徑相匹配的進行路由, 注意:Path 中 P 為大寫
            - After=2022-06-06T03:29:37.318-07:00 # [Asia/Shanghai] # after來進行設定,後面新增一個時間,就是指在什麼時間之後這個服務才可以被存取,否則將無法被存取到
#            - After=2030-01-20T17:42:47.789-07:00 # [America/Denver]

設定成功後再次啟動閘道器,檢視伺服器是否轉發成功。

通過閘道器gateway請求路徑存取查詢使用者資訊:

http://localhost:8081/admin/user/

通過postman測試介面,查詢成功。

4、gateway負載均衡

通過在組態檔中設定路徑的uri為註冊中心伺服器名稱,可以實現gateway負載均衡。存取時通過服務名稱去查詢服務名下面的多個範例,根據設定的策略實現轉發到不同範例服務。

 uri: lb://XXL-JOB-SERVER-MODEL #可以設定為服務名

在通過服務名稱轉發的過程中,有可能轉發失敗。報找不到服務,介面報錯:

檢視閘道器控制檯發現也報錯:

java.net.UnknownHostException: failed to resolve 'xxl-job-server-model' after 4 queries 
    at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.58.Final.jar:4.1.58.Final]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ HTTP POST "/admin/user/" [ExceptionHandlingWebHandler]
Stack trace:
        at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.58.Final.jar:4.1.58.Final]
        at io.netty.resolver.dns.DnsResolveContext.tryToFinishResolve(DnsResolveContext.java:966) ~[netty-resolver-dns-4.1.58.Final.jar:4.1.58.Final]
        at io.netty.resolver.dns.DnsResolveContext.query(DnsResolveContext.java:414) ~[netty-resolver-dns-4.1.58.Final.jar:4.1.58.Final]

沒有找到服務名,剛開始以為是設定路由的服務名和註冊中心的服務名不一致。經過查詢發現是一致。最後查詢發現是使用者端微服務註冊到註冊中心的hostname設定錯誤,需要設定和註冊中心一致的hostname。不然閘道器通過服務名找不到相關地址:

檢視eureka服務註冊地址:

所以需要設定使用者端hostname:

hostname: localhost

再次重新執行系統,發現報錯問題已經解決。