1. 程式人生 > 其它 >14.服務閘道器Zuul和Gateway

14.服務閘道器Zuul和Gateway

zuul分為兩個版本,zuul1已經提更,zuul2還沒開發好!
重點學習gateway!

cloud全家桶中有個很重要的元件就是網管,在1.x版本中採用的時Zuul閘道器
但在2.x版本中,zuul的升級一致跳票,SpringCloud最後自己研發了一個閘道器代替Zuul
這就是Spring Cloud Gateway,一句話:gateway時原zuul1.x版的替代!

gateway是基於非同步非阻塞模型上進行開發的,效能方面不需要擔心!
zuul1是基於Servlet2.5使用阻塞架構上開發的,它不支援任何長連線(如WebSocket)
GateWay的三大核心概念:
    1.Route(路由):路由是構建閘道器的基本模組,它是由ID,目標URI,一系列的斷言和過濾器組成,如果斷言為true則匹配該路由
    2.Predicate(斷言):開發人員可以匹配HTTP請求中的所有內容(例如請求頭或者請求引數),如果請求和斷言相匹配則進行路由
    3.Filter(過濾):指的是spring框架中GatewayFiletr例項,使用過濾器,可以在請求被路由前或者後對請求進行修改!
 
Gateway的核心邏輯就是:路由轉發和執行過濾器鏈 

程式碼構建:
    gateway是一個單獨的專案,去給其他cloud專案訪問加上一層閘道器
1.pom檔案中的內容:
    <dependencies>
        重點:gateway的jar包
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--一般基礎通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.com.springcloud</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
    
2.springboot啟動類:
    @SpringBootApplication
    @EnableEurekaClient
    public class SpringBootGateway9527 {
        public static void main(String[] args) {
            SpringApplication.run(SpringBootGateway9527.class,args);
        }
    }
3.springboot的配置檔案寫法:
server:
  port: 9527
eureka:
  client:
    #表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊資訊,預設為true。單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost:7001/eureka
      # 叢集版
      defaultZone: http://eureka7001:7001/eureka,http://eureka7002:7002/eureka
  instance:
    instance-id: cloud-gateway-service9527
    prefer-ip-address: true

spring:
  application:
    name: cloud-gateway-service
  #重點
  cloud:
    gateway:
      routes:
        - id: producer_queryUserById #路由的id,沒有固定規則但要求唯一,建議符合服務名稱
          uri: http://localhost:8001 #匹配後提供服務的路由地址
          predicates:
            - Path=/producer/queryUserById/** #斷言,服務端路徑匹配的進行路由

        - id: producer_getCurrentThread
          uri: http://localhost:8001
          predicates:
            - Path=/producer/getCurrentThread/**
            
上述配置的意思是:
    當訪問的是gateway:http://localhost:9527/producer/queryUserById/7時,
    滿足上述的斷言,此時會路由到http://localhost:8001//producer/queryUserById/7(路徑拼接)

如果嫌需要給服務專案的每個請求方法都加上閘道器,可以這麼寫:
spring:
  application:
    name: cloud-gateway-service
  cloud:
    gateway:
      routes:
        - id: producer_queryUserById #路由的id,沒有固定規則但要求唯一,建議符合服務名稱
          uri: http://localhost:8001 #匹配後提供服務的路由地址
          predicates:
            重點:服務專案的每個請求前都設定一個公共的路徑/producer,然後根據此進行配置,這樣就不需要單獨每個方法都配置了!
            - Path=/producer/** #斷言,服務端路徑匹配的進行路由
發現上述有問題:   1.服務提供端一般都是以叢集形式部署,在配置檔案中寫死ip和埠不好   2.如何在網管處實現負載均衡呢?
在application.yam中配置如下:
server:
  port: 9527
eureka:
  client:
    #表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊資訊,預設為true。單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      #單機版
      #defaultZone: http://localhost:7001/eureka
      # 叢集版
      defaultZone: http://eureka7001:7001/eureka,http://eureka7002:7002/eureka
  instance:
    instance-id: cloud-gateway-service9527
    prefer-ip-address: true

spring:
  application:
    name: cloud-gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          #重點:加上該配置
          enabled: true #開啟從註冊中心動態建立路由的功能,利用微服務名稱進行路由
      routes:
        - id: producer_queryUserById #路由的id,沒有固定規則但要求唯一,建議符合服務名稱
          #重點2:這裡本身寫服務提供端的地址,此時可以寫服務註冊在eureka註冊中心的名稱,lb:loadbalance(輪訓的負載均衡)
          uri: lb://CLOUD-PROVIDER #匹配後提供服務的路由地址
          predicates:
            - Path=/getPerson/** #斷言,服務端路徑匹配的進行路由

        - id: producer_getCurrentThread
          uri: lb://CLOUD-PROVIDER
          predicates:
            - Path=/producer/timesleep/**
            
測試:
    此時若通過http://localhost:9527/getPerson/10007去訪問時,發現是輪訓的去訪問服務叢集!

predicates講解

參考地址:https://blog.csdn.net/u012367513/article/details/86356708
    predicates(斷言):可以加很多種規則,目的就是讓請求過來找到合適的Route進行處理
    

1. After Route Predicate Factory:使用的是時間作為匹配規則,只要當前時間大於設定時間,路由才會匹配請求。
    spring:
      cloud:
        gateway:
          routes:
          - id: after_route
            uri: http://www.google.com
            predicates:
            - After=2018-12-25T14:33:47.789+08:00
這個路由規則會在東8區的2018-12-25 14:33:47後,將請求都轉跳到google。

2.Before Route Predicate Factory:使用時間作為匹配規則,只要當前時間小於設定時間,路由才會匹配請求。
    spring:
      cloud:
        gateway:
          routes:
          - id: before_route
            uri: http://www.google.com
            predicates:
            - Before=2018-12-25T14:33:47.789+08:00
這個路由規則會在東8區的2018-12-25 14:33:47前,將請求都轉跳到google。

3.Between Route Predicate Factory:使用兩個時間作為匹配規則,只要當前時間大於第一個設定時間,並小於第二個設定時間,路由才會匹配請求。
    spring:
      cloud:
        gateway:
          routes:
          - id: between_route
            uri: http://www.google.com
            predicates:
            - Between=2018-12-25T14:33:47.789+08:00, 2018-12-26T14:33:47.789+08:00
這個路由規則會在東8區的2018-12-25 14:33:47到2018-12-26 14:33:47之間,將請求都轉跳到google。

4. Cookie Route Predicate Factory:使用的是cookie名字和正則表示式的value作為兩個輸入引數,請求的cookie需要匹配cookie名和符合其中value的正則。
    spring:
      cloud:
        gateway:
          routes:
          - id: cookie_route
            uri: http://www.google.com
            predicates:
            - Cookie=cookiename, cookievalue
路由匹配請求存在cookie名為cookiename,cookie內容匹配cookievalue的,將請求轉發到google。

5.Header Route Predicate Factory:與Cookie Route Predicate Factory類似,也是兩個引數,一個header的name,一個是正則匹配的value。
    spring:
      cloud:
        gateway:
          routes:
          - id: header_route
            uri: http://www.google.com
            predicates:
            - Header=X-Request-Id, \d+
路由匹配存在名為X-Request-Id,內容為數字的header的請求,將請求轉發到google。

6. Host Route Predicate Factory:使用的是host的列表作為引數,host使用Ant style匹配。
    spring:
      cloud:
        gateway:
          routes:
          - id: host_route
            uri: http://www.google.com
            predicates:
            - Host=**.somehost.org,**.anotherhost.org
路由會匹配Host諸如:www.somehost.org 或 beta.somehost.org或www.anotherhost.org等請求。

7. Method Route Predicate Factory:是通過HTTP的method來匹配路由。
    spring:
      cloud:
        gateway:
          routes:
          - id: method_route
            uri: http://www.google.com
            predicates:
            - Method=GET
路由會匹配到所有GET方法的請求。

8.Path Route Predicate Factory:使用的是path列表作為引數,使用Spring的PathMatcher匹配path,可以設定可選變數。
    spring:
      cloud:
        gateway:
          routes:
          - id: host_route
            uri: http://www.google.com
            predicates:
            - Path=/foo/{segment},/bar/{segment}
上面路由可以匹配諸如:/foo/1 或 /foo/bar 或 /bar/baz等
其中的segment變數可以通過下面方式獲取:
PathMatchInfo variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Map<String, String> uriVariables = variables.getUriVariables();
String segment = uriVariables.get("segment");
在後續的GatewayFilter Factories就可以做對應的操作了。

9.Query Route Predicate Factory:可以通過一個或兩個引數來匹配路由,一個是查詢的name,一個是查詢的正則value。
    spring:
      cloud:
        gateway:
          routes:
          - id: query_route
            uri: http://www.google.com
            predicates:
            - Query=baz
路由會匹配所有包含baz查詢引數的請求。
    spring:
      cloud:
        gateway:
          routes:
          - id: query_route
            uri: http://www.google.com
            predicates:
            - Query=foo, ba.
路由會匹配所有包含baz,並且baz的內容為諸如:bar或baz等符合ba.正則規則的請求。

10.RemoteAddr Route Predicate Factory:通過無類別域間路由(IPv4 or IPv6)列表匹配路由。
    spring:
      cloud:
        gateway:
          routes:
          - id: remoteaddr_route
            uri: http://www.google.com
            predicates:
            - RemoteAddr=192.168.1.1/24
上面路由就會匹配RemoteAddr諸如192.168.1.10等請求。

11.Modifying the way remote addresses are resolved

Filter:過濾器

分類:
    1.GatewayFilter
    2.GlobalFilter
    3.上述兩種的配置種太多,不如自己去實現程式碼來的方便

自定義的過濾器:
    1.兩個主要的介面:
    2.能幹嘛;
         2.1全域性日誌記錄
        2.2統一通關鑑權
        
樣例:建立自己的過濾器,加上@Component註解將其載入到容器中
    @Component
    @Slf4j
    重點1:實現兩個介面:GlobalFilter和Ordered(用來控制該過濾器的執行順序,數值越小,越先執行!)
    public class MyLogGateWayFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            重點2:hutool工具包的使用
            log.info("*****gateWay的自定義過濾器:時間:"+ DateUtil.formatDateTime(new Date()));
            重點3:獲取請求中的uname屬性值
            String uanme=exchange.getRequest().getQueryParams().getFirst("uname");
            if(uanme == null){
                log.info("********使用者名稱為null,非法使用者,/(ㄒoㄒ)/~~");
                重點4:設定返回的狀態碼:406
                exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
                重點5:表示訊息處理完畢,可以進行結束
                return exchange.getResponse().setComplete();
            }
            重點6:該該條過濾的ServerWebExchange 物件傳入到過濾鏈中的下一個過濾環節!
            return chain.filter(exchange);
        }
        重點7:過濾的執行順序,數字越小,越先執行!
        @Override
        public int getOrder() {
            return 0;
        }
    }