SpringCloud學習第八篇:gateway學習(Hoxton.SR4)

網關的主要作用

  • 協議轉換,路由轉發
  • 流量聚合,對流量進行監控,日誌輸出
  • 作爲整個系統的前端工程,對流量進行控制,有限流的作用
  • 作爲系統的前端邊界,外部流量只能通過網關才能訪問系統
  • 可以在網關層做權限的判斷
  • 可以在網關層做緩存

gateway的請求過程

在這裏插入圖片描述
​ 客戶端向Spring Cloud Gateway發出請求。 如果Gateway Handler Mapping確定請求與路由匹配(這個時候就用到predicate),則將其發送到Gateway web handler處理。 Gateway web handler處理請求時會經過一系列的過濾器鏈。 過濾器鏈被虛線劃分的原因是過濾器鏈可以在發送代理請求之前或之後執行過濾邏輯。 先執行所有“pre”過濾器邏輯,然後進行代理請求。 在發出代理請求之後,收到代理服務的響應之後執行“post”過濾器邏輯。這跟zuul的處理過程很類似。在執行所有“pre”過濾器邏輯時,往往進行了鑑權、限流、日誌輸出等功能,以及請求頭的更改、協議的轉換;轉發之後收到響應之後,會執行所有“post”過濾器的邏輯,在這裏可以響應數據進行了修改,比如響應頭、協議的轉換等。

三個關鍵字

  • Route: 請求到網關之後轉發到那個服務。使用ID標識,路由到哪裏的Url地址。還有斷言和過濾器的集合。
  • Predicate(斷言): 是java8的斷言函數,由開發人員去匹配當此請求的任何信息。根據請求信息路由到那個服務。
  • Fiilter(過濾器): 請求和響應都可以在過濾器中都可以修改。

整合註冊中心

  • maven
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
  • application.yml
server:
  port: 1105
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:1109
    gateway:
      discovery:
        locator:
          # true 表明gateway開啓服務註冊和發現的功能,並且spring cloud gateway自動根據服務發現爲每一個服務創建了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務。
#如果使用自定義的路由規則需要將這裏設置爲false 不然會有兩個路由
          enabled: true
   # 是將請求路徑上的服務名配置爲小寫(因爲服務註冊的時候,向註冊中心註冊時將服務名轉成大寫的了
          lowerCaseServiceId: true  
  • 啓動配置
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class, args);
    }
}
  • 啓動報錯
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.

原因:
依賴衝突,spring-cloud-starter-gateway與spring-boot-starter-web和spring-boot-starter-webflux依賴衝突
解決:
排除 spring-boot-starter-web和spring-boot-starter-webflux依賴

 <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
    </exclusions>
  • 增加一個web-demo工程註冊到nacos就可以了

predicate(斷言)簡介

在上面的處理過程中,有一個重要的點就是講請求和路由進行匹配,這時候就需要用到predicate,它是決定了一個請求走哪一個路由。
在這裏插入圖片描述
在上圖中,有很多類型的Predicate,比如說時間類型的Predicated,當只有滿足特定時間要求的請求會進入到此predicate中,並交由router處理;cookie類型的,指定的cookie滿足正則匹配,纔會進入此router;以及host、method、path、querparam、remoteaddr類型的predicate,每一種predicate都會對當前的客戶端請求進行判斷,是否滿足當前的要求,如果滿足則交給當前請求處理。如果有很多個Predicate,並且一個請求滿足多個Predicate,則按照配置的順序第一個生效。

//在web-demo寫一個請求
@GetMapping(value = "/a")
    public String a() {
        return "demo a";
    }
//gateway網關配置
	routes:
        #id標籤配置的是router的id,每個router都需要一個唯一的id,
        - id: api-a
          uri: lb://web-demo
          #當是get請求的時候跳轉到web-demo服務,會將url後面的請求路徑攜帶過去
          predicates:
            - Method=GET
//url訪問 http://localhost:1105/a

還有很多斷言可以使用,可以參考官方文檔

Fiilter

​ 當我們有很多個服務時,客戶端請求各個服務的Api時,每個服務都需要做相同的事情,比如鑑權、限流、日誌輸出等。對於這種重複操作的工作,在微服務的上一層加一個全局的權限控制、限流、日誌輸出的Api Gatewat服務,然後再將請求轉發到具體的業務服務層。這個Api Gateway服務就是起到一個服務邊界的作用,外接的請求訪問系統,必須先通過網關層。
​ Spring Cloud Gateway同zuul類似,有“pre”和“post”兩種方式的filter。客戶端的請求先經過“pre”類型的filter,然後將請求轉發到具體的業務服務,收到業務服務的響應之後,再經過“post”類型的filter處理,最後返回響應到客戶端。
​ 與zuul不同的是,filter除了分爲“pre”和“post”兩種方式的filter外,在Spring Cloud Gateway中,filter從作用範圍可分爲另外兩種,一種是針對於單個路由的gateway filter,它在配置文件中的寫法同predict類似;另外一種是針對於所有路由的global gateway filer。
在這裏插入圖片描述

  • gateway提供的過濾器
//請求頭增加參數
//web-demo服務增加請求
    @GetMapping(value = "/demo")
    public String addRequestHeader(@RequestHeader(value = "X-test", required = false) String username) {
        System.out.println("X-test:" + username);
        return "demo SUCCESS";
    }

//網關配置
      routes:
        #id標籤配置的是router的id,每個router都需要一個唯一的id,
        - id: api-a
          #lb就是負載均衡從註冊中心獲取WEB-DEMO
          uri: lb://web-demo
          predicates:
            - Method=GET
          filters:
            #經過次過濾器的時候會在請求頭上增加X-test值爲test
            - AddRequestHeader=X-test, test
                
//url訪問 http://localhost:1105/demo
//訪問路徑重寫
//web-demo服務增加請求
    @GetMapping(value = "/demo/path")
    public String demoPath() {
        System.out.println("demoPath SUCCESS");
        return "demoPath SUCCESS";
    }
//網關配置
      routes:
        #id標籤配置的是router的id,每個router都需要一個唯一的id,
        - id: api-a
          uri: lb://web-demo
          predicates:
            #根據路由斷言
            - Path=/a/**
          filters:
            #路徑重寫 網關訪問的是/a/**  現在改寫爲/demo/**調用WEB-DEMO服務
            - RewritePath=/a/(?<segment>.*),/demoPath/$\{segment}
//url訪問 http://localhost:1105/a/path

更多過濾器,可以參考官方文檔

自定義Fiilter

  • 局部過濾器

使用GatewayFilter,將應用註冊到單個路由或一個分組的路由上

//過濾器配置
@Component
@Slf4j
public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {

    private static final String KEY = "flag";

    /**
     * 封裝yml傳進來的參數
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(KEY);
    }

    public RequestTimeGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 實現攔截邏輯
     */
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //pre過濾器
            exchange.getAttributes().put("requestTimeGateway", System.currentTimeMillis());
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> {
                        //post過濾器
                        Long startTime = exchange.getAttribute("requestTimeGateway");
                        if (startTime != null) {
                            log.debug("請求路徑:{},耗時:{}ms",exchange.getRequest().getURI().getRawPath(),System.currentTimeMillis() - startTime);
                            if (config.isFlag()) {
                                log.debug("參數:{}",exchange.getRequest().getQueryParams());
                            }
                        }
                    })
            );
        };
    }

    /**
     * 參數
     */
    public static class Config {
        private boolean flag;
        public boolean isFlag() {
            return flag;
        }
        public void setFlag(boolean flag) {
            this.flag = flag;
        }

    }
}
      routes:
        #id標籤配置的是router的id,每個router都需要一個唯一的id,
        - id: api-a
          uri: lb://web-demo
          predicates:
            #根據路由斷言
            - Path=/a/**
          filters:
            #路徑重寫 網關訪問的是/a/**  現在改寫爲/demo/**調用web-demo服務
            - RewritePath=/a/(?<segment>.*),/demo/$\{segment}
            #是否開啓請求參數的輸出
            - RequestTime=false
  • 全局過濾器

可用在鑑權,日誌攔截等。無需在配置文件中配置,作用在所有路由上。最終通過GatewayFilterAdapter包裝成GatewayFilterChain可識別的過濾器,它爲請求業務以及路由的URI轉換爲真實業務服務的請求地址的核心過濾器,不需要配置,系統初始化時加載,並作用在每個路由上。

@Component
@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.debug("pre:token ");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.debug("post類型的過濾器");
        }));
    }
    @Override
    public int getOrder() {
        return 0;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章