天天看點

09_GateWay—網關介紹GateWay—網關介紹

GateWay—網關介紹

所謂的API網關,就是指系統的統一入口,它封裝了應用程式的内部結構,為用戶端提供統一服務,一些與業務本身功能天關的公共邏輯可以在這裡實作,諸如認證、簽權、監控、路由轉發等等。添加上API網關之後,系統的架構圖變成了如下所示:

09_GateWay—網關介紹GateWay—網關介紹

優點:

09_GateWay—網關介紹GateWay—網關介紹

整體微服務結構圖,網關Gateway的位置

09_GateWay—網關介紹GateWay—網關介紹

GateWay介紹

網關作為流量的入口,常用的功能包括路由轉發,權限校驗,限流等。

Spring Cloud Gateway 是Spring Cloud官方推出的第二代網關架構,定位于取代NetflexZuul 1.0。相比Zuul來說,Spring Clout Gateway)提供更優秀的性能,更強大的有功能

Spring Cloud Gateway是由WebFlux + Netty + Reactor實作的響應式的APl網關。它不能在傳統的 servlet容器中工作,也不能建構成war包.

Spring Clout Gateway旨在為微服務架構提供一種簡單且有效的API路由的管理方式,并基于Filter的方式提供網關的基本功能,例如說安全認證、監控、限流等等。

功能特征

  • 基于Spring Framework 5,Project Reactor和 Spring Boot 2.0進行建構;
  • 動态路由:能夠比對任何請求屬性;
  • 支援路徑重寫;
  • 內建 Spring Cloud服務發現功能(Nacos、Eruka) ;
  • 可內建流控降級功能(Sentinel、Hystrix) ;
  • 可以對路由指定易于編寫的 Predicate (斷言)和Filter(過濾器);

核心概念:

  • 路由

路由是網關中最基礎的部分,路由資訊包括一個ID、一個目的URI、一組斷言工廠、一組Fiter組成。如果斷言為真,則說明請求的URL和配置的路由比對.

  • 斷言(predicates)

Java8中的斷言函數,SprigCloud Gateway中的斷言函數類型是Sping5 o架構中的SererWebExchange。斷言函數允許開發者去定義比對Http request中的任何資訊,比如請求頭和參數等。

  • 過濾器(Fllter)

SpringCloud Gateway中的filter分為Gateway Filler和Global Filter。Filter可以對請求和響應進行處理。

GateWay—初體驗

1.建立新的子項目gateway 引入依賴

<!--   gateway的依賴 springcloud開發 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
           

2.編寫配置檔案

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    #gateway的配置
    gateway:
      #路由配置
      routes:
        - id: order_route  #路由的唯一辨別,路由到order
          uri: http://localhost:8020  #需要轉發的位址
          #斷言規則  用于路由規則的比對
          predicates:
            - Path=/order-serv/**
            # 請求 http://localhost:8088/order-serv/order/add路由到
            # http://localhost:8020/order-serv/order/add
          filters:
            - StripPrefix=1  #轉發之前去掉第一層路徑
            # http://localhost:8020/order/add
           

3.主啟動類啟動

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

通路之前啟動 order-nacos 和 stock-nacos子產品

order-nacos子產品 controller代碼

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/add")
    public String add(){
        System.out.println("下單成功!");
        String msg = restTemplate.getForObject("http://stock-service/stock/reduct", String.class);
        return "hello world"+msg;
    }
}
           

stock-nacos子產品 controller代碼

@RestController
@RequestMapping("/stock")
public class StockController {

    @Value("${server.port}")
    String port;

    @RequestMapping("/reduct")
    public String reduct(){
        System.out.println("扣減庫存");
        return "扣減庫存"+port;
    }
}
           

三個子產品全部啟動

測試通路 http://localhost:8088/order-serv/order/add

頁面顯示 hello world扣減庫存8021

GateWay整合nacos

現在在配置檔案中寫死了轉發路徑的位址,前面我們已經分析過位址寫死帶來的問題,接下來我們從注冊中心擷取此位址。

內建Nacos

1.再引入依賴

<!--    nacos服務注冊發現 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
           

2.編寫yml配置檔案

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    #gateway的配置
    gateway:
      #路由配置
      routes:
        - id: order_route  #路由的唯一辨別,路由到order
          uri: lb://order-service  #需要轉發的位址 lb 使用nacos本地負載均衡政策 order-service 服務名  
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1  
    #配置Nacos
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos

           

測試通路 http://localhost:8088/order-serv/order/add

頁面顯示 hello world扣減庫存8021

簡寫形式

  • 比較少用
  • 自己去配置斷言和過濾器比較好
server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    #gateway的配置
    gateway:
      discovery:
        locator:
          enabled: true #是否啟動自動識别nacos服務
    #路由配置

    #配置Nacos
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos

           

測試通路 http://localhost:8088/order-service/order/add 用的是服務名

頁面顯示 hello world扣減庫存8021

GateWay—内置路由斷言工廠

作用 : 當請求gateway的時候,使用斷言對請求進行比對,如果比對成功就路由轉發,如果比對失敗就傳回404

類型 内置 、自定義

  • 基于Datetime類型的斷言工廠

此類型的斷言根據時間做判斷,主要有三個:

AfterRoutePredicateFactory:接收一個日期參數,判斷請求日期是否晚于指定日期

BeforeRoutePredicateFactory:接收一個日期參數,判斷請求日期是否早于指定日期

BetweenRoutePredicateFactory:接收兩個日期參數,判斷請求日期是否在指定時間段内

predicates:
	- Path=/order-serv/**
	- After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
           
  • 基于遠端位址的斷言工廠

RemoteAddrRoutePredicateFactory:接收一個IP位址段,判斷請求主機位址是否在位址段中

- RemoteAddr=192.168.1.1/24
           
  • 基于Cookie的斷言工廠

CookieRoutePredicateFactory: 接收兩個參數,cookie名字和一個正規表達式。判斷請求cookie是否具有給定名稱且值與正規表達式比對。

- Cookie=chocolate,ch.
           
  • 基于Header的斷言工廠

HeaderRoutePredicateFactory:接收兩個參數,标題名稱和正規表達式。判斷請求Header是否員有給定名稱且值與正規表達式比對.

- Header=X-Request-Id,\d+
           
  • 基于Host的斷言工廠

HostRoutePredicateFactory:接收一個參數,主機名模式。判斷請求的Host是否滿足比對規則

- Host=**.testhost.org
           
  • 基于Method請求方法的斷言工廠

MethodRoutePredicateFactory:接收一個參數,判斷請求類型是否跟指定的類型比對。

- Method=GET
           
  • 基于Path請求路徑的斷言工廠

PathRoutePredicateFactory:接收一個參數,判斷請求的URI部分是否滿足路徑規則,

  • 基于Query請求參數的斷言工廠
- Query=name,xushu|zhuge  
#要有name參數 而且要等于xushu或者zhuge
           

GateWay—自定義路由斷言工廠

自定義路由斷言工廠需要繼承

AbstractRoutePredicateFactory

類,重寫 apply 方法的邏輯。在apply方法中可以通過 exchange.getRequest()拿到ServerhttptRequest對象,進而可以擷取到請求的參數、請求方式、請求頭等資訊.

1.必須spring元件 bean

2.類必須加上

RoutePredicateFactory

作為結尾

3.必須繼承

AbstractRoutePredicateFactory

4.必須聲明靜态内部類 聲明屬性來接受 配置檔案中的對應的斷言的資訊

5.需要結合

shortcutFieldOrder

進行綁定

6.通過apply進行邏輯判斷 true就是比對成功 false比對失敗

寫個自定義參數叫CheckAuth 值必須是xushu才可以通路
package com.tian.predicates;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {


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

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name");
    }

    public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            public boolean test(ServerWebExchange exchange) {
                if(config.getName().equals("xushu")){
                    return true;
                }
                return false;
            }

        };
    }
//用來接收配置檔案中 斷言的資訊
@Validated
public static class Config {
private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
}

           

配置檔案

predicates:
#- Path=/order-serv/**
	- Path=/order/**
	- CheckAuth=xushu
	#- CheckAuth=xushu2
           
通路 http://localhost:8088/order/add xushu就可以正确通路,xushu2頁面就會報404

GateWay—局部(内置、自定義)過濾器

添加請求頭

#gateway子產品配置檔案
spring:
  cloud:
    gateway:
      routes:
      - id: order_route
      uri: http://localhost:8020
      filters:
       - AddRequestHeader=X-Request-color,red  #添加請求頭
           

order-nacos子產品controller

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color){
    return color;
           

通路 http://localhost:8088/order/header 頁面顯示 red

09_GateWay—網關介紹GateWay—網關介紹
09_GateWay—網關介紹GateWay—網關介紹
09_GateWay—網關介紹GateWay—網關介紹

自定義過濾器工廠

  • 和斷言工廠差不多
09_GateWay—網關介紹GateWay—網關介紹

配置檔案

filters:
 - CheckAuth=fox,男
           
通路輸出CheckAuthGatewayFilterFactory===fox: 男

GateWay—全局過濾器

局部過濾器和全同過濾器差別:

局部:局部針對某個路由,需要在路由中進行配置

全局:針對所有路由請求,你定義就會投入使用

自定義全局過濾器

@Component
public class LogFilter implements GlobalFilter {
    Logger log= LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

           

GateWay—請求日記記錄&跨域處理

要啟用Reactor Netty 通路日志,請設定

-Dreactor.netty.http.server.accessLogEnabled=true.

09_GateWay—網關介紹GateWay—網關介紹
重新開機 控制台就會列印輸出日志

它必須是Java系統屬性,而不是 Spring Boot屬性。

跨域問題

配置檔案application.yml

#跨域配置
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':  #允許跨域通路的資源
            allowedOrigins: "https://docs.spring.io" #跨域允許來源
            allowedMethods:
            - GET
            - POST
           

配置類配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter(){
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*"); //允許的method
        config.addAllowedOrigin("*"); //允許的來源
        config.addAllowedHeader("*"); //允許的請求頭參數
        // 運作通路的資源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",config);
        return new CorsWebFilter(source);
    }

}
           

GateWay—整合Sentinel流控降級

api-gateway子產品

1.添加依賴

<!--    sentinel整合gateway    -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--    sentinel的依賴     -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
           

2.配置檔案

spring:
  cloud:
  	  #配置sentinel
    sentinel:
      transport:
        dashboard: 127.0.0.1:8888
           

3.啟動sentinel 控制台配置規則

09_GateWay—網關介紹GateWay—網關介紹

4.通路

http://localhost:8088/order/add

現象 前兩次正常頁面顯示 第三次頁面顯示

Blocked by Sentinel: ParamFlowException
           

GateWay—整合Sentinel流控降級詳細配置

09_GateWay—網關介紹GateWay—網關介紹

API管理

09_GateWay—網關介紹GateWay—網關介紹
09_GateWay—網關介紹GateWay—網關介紹

降級

09_GateWay—網關介紹GateWay—網關介紹

處理方法(在降級規則那配置)

09_GateWay—網關介紹GateWay—網關介紹

自定義異常

  • 讓頁面顯示自定義異常資訊
package com.tian.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;

@Configuration
public class GatewayConfig {
    @PostConstruct
    public void init(){
        BlockRequestHandler blockRequestHandler=new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {

                HashMap<String, String> map = new HashMap<>();
                map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
                map.put("message","限流了");
                //自定義異常處理
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

           

或 yml配置

spring:
  cloud:
  	sentinel:
  	  scg:
        fallback:
          mode: response
          response-body: "{code:'',message:''}"