Spring Cloud Alibaba - Gateway

Gateway

Gateway簡介

image-20210726105445118

image-20210725203850382

底層使用Netty框架,性能大於Zuuljavascript

image-20210725204038223

配置gateway模塊,通常使用yaml格式:html

server:
  port: 80

#spring boot actuator服務監控端點
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    jmx:
      exposure:
        include: '*'
    web:
      exposure:
        include: '*'
  health:
    sentinel:
      enabled: false

spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #啓用DiscoveryClient網關集成的標誌,能夠實現服務的發現
      #gateway 定義路由轉發規則
      routes:
          #一份設定
        - id: route1   #惟一標識
          uri: lb://nacos-discovery-provider  #訪問的路徑,lb://負載均衡訪問固定寫法,經過負載均衡調取全部設定中的一份
          predicates: #謂詞,判斷,是否匹配。用戶請求的路徑是否與...進行匹配,若是匹配則能夠訪問,不然就404
            - Path=/test,/index
        - id: route2
          uri: lb://nacos-discovery-provider
          predicates:
            - Path=/test*/**
        - id: route3
          uri: lb://nacos-discovery-provider
          predicates:
            - Path=/service/**
          #過濾器
          filters:
            - AddRequestHeader=X-Request-Id, 12345

    nacos:
      discovery:
        server-addr: localhost:8848
        password: nacos
        username: nacos
    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080

啓動類中添加註解:前端

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {

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

啓動測試,在這裏可能會遇到版本衝突的問題:可使用ctrl+alt+shift+u ,而後出現以下圖所示,紅色就是就是證實存在jar包衝突java

在這裏插入圖片描述


Gateway核心概念

路由:網關的基本構建組成,它由ID,目標URI,謂詞集合和過濾器集合定義,若是集合謂詞爲true,則匹配路由,不然不匹配;web

謂詞:這是Java 8函數謂詞,輸入類型是Spring Framework ServerWebExchange,能夠匹配HTTP請求中的全部內容,例如請求頭或參數;正則表達式

過濾器:這些是使用特定工廠構造的Spring Framework GatewayFilter實例,能夠在發送給下游請求以前或以後修改請求和響應;spring

下圖從整體上概述了Spring Cloud Gateway的工做方式:json

image-20210726105612268

客戶端向Spring Cloud Gateway發出請求,若是網關處理程序映射肯定請求與路由匹配,則將其發送到網關Web處理程序,該處理程序經過特定於請求的過濾器鏈運行請求,篩選器由虛線分隔的緣由是,篩選器能夠在發送代理請求以前和以後運行邏輯,全部「前置」過濾器邏輯均被執行,而後發出代理請求,發出代理請求後,將運行「後」過濾器邏輯;在沒有端口的路由中定義URI,HTTP和HTTPS URI的默認端口值分別是80和443後端


路由謂詞工廠

Spring Cloud Gateway將路由匹配做爲Spring WebFlux HandlerMapping基礎架構的一部分,Spring Cloud Gateway包括許多內置的路由謂詞工廠,全部這些謂詞都與HTTP請求的不一樣屬性匹配,能夠將多個路由謂詞工廠結合使用;跨域

總共有11個路由謂詞工廠:

\1. The After Route Predicate Factory

\2. The Before Route Predicate Factory

\3. The Between Route Predicate Factory

\4. The Cookie Route Predicate Factory

\5. The Header Route Predicate Factory

\6. The Host Route Predicate Factory

\7. The Method Route Predicate Factory

\8. The Path Route Predicate Factory

\9. The Query Route Predicate Factory

\10. The RemoteAddr Route Predicate Factory

\11. The Weight Route Predicate Factory

After路由謂詞工廠

After route謂詞工廠採用一個參數,即datetime(這是一個Java ZonedDateTime),該謂詞匹配在指定日期時間以後發生的請求,如下示例配置了路由後謂詞:

img

這條路由符合2017年1月20日17:42:47時間([America/Denver])以後的任何請求;

時間經過獲取:System.out.println(ZonedDateTime.now());

Before路由謂詞工廠

Before路由謂詞工廠採用一個參數,即datetime(這是一個Java ZonedDateTime),該謂詞匹配在指定日期時間以前發生的請求,下面的示例配置路由以前謂詞:

img

這條路由符合2017年1月20日17:42:47時間([America/Denver])以前的任何請求;

Between路由謂詞工廠

路由謂詞之間的工廠使用兩個參數datetime1和datetime2,它們是java ZonedDateTime對象,該謂詞匹配在datetime1以後和datetime2以前發生的請求,datetime2參數必須在datetime1以後,如下示例配置了路由之間的謂詞:

image-20210726110815974

該路線與2017年1月20日山區時間(丹佛)以後和2017年1月21日17:42山區時間(丹佛)以前的任何請求相匹配,這對於維護時段可能頗有用;

Cookie路由謂詞工廠採用兩個參數,即cookie名稱和一個regexp(這是Java正則表達式),該謂詞匹配具備給定名稱且其值與正則表達式匹配的cookie,如下示例配置Cookie路由謂詞工廠:

img

此路由匹配具備名爲Chocolate的cookie的請求,該cookie的值與ch.p正則表達式匹配;

舉例:curl http://192.168.0.104/index --cookie token=123456

Header 路由謂詞工廠

header 路由謂詞工廠使用兩個參數,header 名稱和一個regexp(這是Java正則表達式),該謂詞與具備給定名稱的header 匹配,該header 的值與正則表達式匹配,如下示例配置標頭路由謂詞:

img

若是請求具備名爲X-Request-Id的標頭,且其值與\ d +正則表達式匹配(即,其值爲一個或多個數字),則此路由匹配;

舉例:curl http://192.168.0.104/index --header "X-Request-Id:19228"

Host 路由謂詞工廠

host路由謂詞工廠使用一個參數:主機名模式列表,如下示例配置主機路由謂詞:

img

還支持URI模板變量(例如{sub} .myhost.org),若是請求的主機標頭的值爲www.somehost.org或beta.somehost.org或www.anotherhost.org,則此路由匹配;

Method 路由謂詞工廠

方法路由謂詞工廠使用方法參數,該參數是一個或多個參數:要匹配的HTTP方法,如下示例配置方法route謂詞:

img

若是請求方法是GET或POST,則此路由匹配;

Path路由謂詞工廠

路徑路由謂詞工廠使用兩個參數:Spring PathMatcher模式列表和一個稱爲matchOptionalTrailingSeparator的可選標誌,如下示例配置路徑路由謂詞:

img

若是請求路徑爲例如/red/1或/red/blue或/blue/green,則此路由匹配;

Query路由謂詞工廠

查詢路由謂詞工廠採用兩個參數:必需的參數和可選的regexp(這是Java正則表達式),如下示例配置查詢路由謂詞:

img

若是請求包含green查詢參數,則前面的路由匹配;

img

若是請求包含值與gree匹配的red查詢參數,則上述路由匹配;

RemoteAddr 路由謂詞工廠

RemoteAddr路由謂詞工廠使用源列表(最小大小爲1),這些源是標記(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子網掩碼)),下面的示例配置RemoteAddr路由謂詞:

img

若是請求的遠程地址是例如192.168.1.10,則此路由匹配;

Weight 路由謂詞工廠

權重路由謂詞工廠採用兩個參數:group和weight(一個int),權重是按組計算的,如下示例配置權重路由謂詞:

img

這條路由會將約80%的流量轉發至weight_high.org,並將約20%的流量轉發至weight_low.org;


GatewayFilter 工廠

路由過濾器容許以某種方式修改傳入的HTTP請求或傳出的HTTP響應,Spring Cloud Gateway包括許多內置的GatewayFilter工廠;

img

總共有31個GatewayFilter工廠:

\1. The AddRequestHeader GatewayFilter Factory

\2. The AddRequestParameter GatewayFilter Factory

\3. The AddResponseHeader GatewayFilter Factory

\4. The DedupeResponseHeader GatewayFilter Factory

\5. The Hystrix GatewayFilter Factory

\6. Spring Cloud CircuitBreaker GatewayFilter Factory

\7. The FallbackHeaders GatewayFilter Factory

\8. The MapRequestHeader GatewayFilter Factory

\9. The PrefixPath GatewayFilter Factory

\10. The PreserveHostHeader GatewayFilter Factory

\11. The RequestRateLimiter GatewayFilter Factory

\12. The RedirectTo GatewayFilter Factory

\13. The RemoveRequestHeader GatewayFilter Factory

\14. RemoveResponseHeader GatewayFilter Factory

\15. The RemoveRequestParameter GatewayFilter Factory

\16. The RewritePath GatewayFilter Factory

\17. RewriteLocationResponseHeader GatewayFilter Factory

\18. The RewriteResponseHeader GatewayFilter Factory

\19. The SaveSession GatewayFilter Factory

\20. The SecureHeaders GatewayFilter Factory

\21. The SetPath GatewayFilter Factory

\22. The SetRequestHeader GatewayFilter Factory

\23. The SetResponseHeader GatewayFilter Factory

\24. The SetStatus GatewayFilter Factory

\25. The StripPrefix GatewayFilter Factory

\26. The Retry GatewayFilter Factory

\27. The RequestSize GatewayFilter Factory

\28. The SetRequestHost GatewayFilter Factory

\29. Modify a Request Body GatewayFilter Factory

\30. Modify a Response Body GatewayFilter Factory

\31. Default Filters


Gateway自定義謂詞

Spring Cloud Gateway內置了一系列的路由謂詞工廠,可是若是這些內置的路由謂詞工廠不能知足業務需求的話,能夠自定義路由謂詞工廠來實現特定的需求;

下面列舉兩個例子:

一、要求請求必須攜帶一個token,而且token值等於指定的值,才能訪問;
二、要求某個服務的用戶只容許在23:00 - 6:00這個時間段內才能夠訪問;

自定義謂詞具體步驟:

(1)首先定義一個配置類,用於承載配置參數;

(2)定義一個路由謂詞工廠;

注:TokenRoutePredicateFactory類,前面的Token與.yml配置文件裏面配置的名字對應,後面的RoutePredicateFactory名字是固定的,不能隨便寫,這是Spring Cloud Gateway的約定,類名須爲「謂詞工廠名(好比:Token)」 + RoutePredicateFactory

(3)在配置文件中啓用該路由謂詞工廠,即配置一個Token=123456;

時間格式不是隨便配置,而是Spring Cloud Gateway的默認時間格式,採用JDK8裏面的格式化:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.*ofLocalizedTime*(FormatStyle.*SHORT*); String nowTime = dateTimeFormatter.format(ZonedDateTime.*now*()); System.*out*.println(nowTime);

到此爲止就實現了一個自定義路由謂詞工廠,若此時token值不相等,不在容許的訪問時間段內,訪問就會報404;

以Token爲例:

配置類:

@Data //lombok
public class TokenConfig {
    //獲取的是配置文件中設置的token
    private String token;
}

自定義路由謂詞工廠:

@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> {

    public TokenRoutePredicateFactory() {
        super(TokenConfig.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("token");
    }

    @Override
    public Predicate<ServerWebExchange> apply(TokenConfig tokenConfig) {
        // (T t) -> true
        return exchange -> {
            MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();//獲取請求中的token字段的值

            boolean flag = false;

            List<String> list = new ArrayList<>();

            valueMap.forEach((k, v) -> {
                list.addAll(v);
            });

            for (String s : list) {
                log.info("Token -> {} ", s);
                if (StringUtils.equalsIgnoreCase(s, tokenConfig.getToken())) {
                    flag = true;
                    break;
                }
            }
            return flag;
        };
    }
}

若是謂詞不匹配時,處理返回404頁面顯然不合規範。須要咱們對404進行處理

處理的頂層接口是WebExceptionHandler

默認實現是DefaultErrorWebExceptionHandler

img

咱們須要覆蓋它的默認實現DefaultErrorWebExceptionHandler,覆蓋裏面的方法getRoutingFunction,getHttpStatus,在方法裏面編寫咱們想要返回的結果

實現類:

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                      ResourceProperties resourceProperties,
                                      ErrorProperties errorProperties,
                                      ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    /**
     * 獲取異常屬性
     */
    /*@Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        int code = 500;
        Throwable error = super.getError(request);
        if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) {
            code = 404;
        }
        return response(code, this.buildMessage(request, error));
    }*/

    /**
     * 指定響應處理方法爲JSON處理的方法
     *
     * @param errorAttributes
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    /**
     * 根據code獲取對應的HttpStatus
     * @param errorAttributes
     */
    @Override
    protected int getHttpStatus(Map<String, Object> errorAttributes) {
        int statusCode = (int) errorAttributes.get("status");
        return statusCode;
    }

    /**
     * 構建異常信息
     * @param request
     * @param ex
     * @return
     */
    private String buildMessage(ServerRequest request, Throwable ex) {
        StringBuilder message = new StringBuilder("Failed to handle request [");
        message.append(request.methodName());
        message.append(" ");
        message.append(request.uri());
        message.append("]");
        if (ex != null) {
            message.append(": ");
            message.append(ex.getMessage());
        }
        return message.toString();
    }

    /**
     * 構建返回的JSON數據格式
     *
     * @param status      狀態碼
     * @param errorMessage  異常信息
     * @return
     */
    public static Map<String, Object> response(int status, String errorMessage) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("message", errorMessage);
        map.put("data", null);
        return map;
    }
}

除此以外,還須要寫一個配置類,添加相關配置,使得404時,將相關信息給到上面編寫的處理異常類中,並將該異常類返回。

@Configuration
public class GatewayConfiguration {

    private final ServerProperties serverProperties;

    private final ApplicationContext applicationContext;

    private final ResourceProperties resourceProperties;

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;
    //構造函數
    public GatewayConfiguration(ServerProperties serverProperties,
                                ApplicationContext applicationContext,
                                ResourceProperties resourceProperties,
                                ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        //spring會將這些值從容器中獲取到
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
    
     @Bean("myErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes) {

        MyErrorWebExceptionHandler exceptionHandler = new MyErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);

        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

Gateway自定義網關過濾器

網關過濾器的頂層接口是GatewayFilterFactory。一般狀況下能夠繼承AbstractGatewayFilterFactory實現自定義網關過濾器;或者繼承AbstractNameValueGatewayFilterFactory,該方式配置方式更簡單,而後覆蓋裏面的一個方法apply

/**
 * 自定義filter
 */
@Slf4j
@Component
//類名中FilterFactory固定不可更改成規約
public class RequestLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {

        return (ServerWebExchange exchange, GatewayFilterChain chain) -> {
            log.info("請求網關,{}, {}", config.getName(), config.getValue());
            MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
            valueMap.forEach((k, v) -> {
                log.info("請求參數 {} ", k);
                v.forEach(s -> {
                    log.info("請求參數值 = {} ", s);
                });
            });
            return chain.filter(exchange);
        };
    }
}

Gateway全局過濾器

上面的過濾器工廠是執行在指定路由之上,能夠稱爲路由過濾器(或者局部過濾器),而全局過濾器是做用於全部的路由上,對全部的路由進行過濾;

全局過濾器的頂層接口是GlobalFilter ,和GatewayFilter 有同樣的接口定義,只不過GlobalFilter 會做用於全部路由;

全局過濾器有執行順序問題,經過getOrder()方法的返回值決定執行順序,數值越小越靠前執行;

Spring cloud gateway默認內置了不少全局過濾器,好比:

\1. Combined Global Filter and GatewayFilter Ordering

\2. Forward Routing Filter

\3. The LoadBalancerClient Filter

\4. The ReactiveLoadBalancerClientFilter

\5. The Netty Routing Filter

\6. The Netty Write Response Filter

\7. The RouteToRequestUrl Filter

\8. The Websocket Routing Filter

\9. The Gateway Metrics Filter

\10. Marking An Exchange As Routed

固然咱們也能夠自定義全局過濾器

@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("全局Filter請求......");

        MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
        valueMap.forEach((k, v) -> {
            log.info("全局Filter攔截參數 {} ", k);
            v.forEach(s -> {
                log.info("全局Filter攔截參數值 = {} ", s);
            });
        });
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;//越小越先執行
    }
}

一經實現,就生效。只要知足實現要求,就實現全局過濾。


Gateway集成Ribbon實現負載均衡

實現原理是在全局LoadBalancerClientFilter中進行攔截,而後再該過濾器中依賴LoadBalancerClient loadBalancer,而此負載均衡接口的具體實現是RibbonLoadBalancerClient,即spring cloud gateway已經整合好了ribbon,已經能夠實現負載均衡,咱們不須要作任何工做,網關對後端微服務的轉發就已經具備負載均衡功能;


Gateway集成Sentinel

網關集成Sentinel是爲了流控熔斷降級,具體集成整合步驟以下:

一、添加依賴;

*

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
   <version>1.7.2</version>
</dependency>

二、在gateway配置文件中添加sentinel控制檯配置;

spring:
  cloud:
    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080

三、寫代碼,在spring容器中配置一個Sentinel的全局過濾器;

@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
    return new SentinelGatewayFilter();
}

四、能夠進行測試;

自定義網關限流返回信息

在Gateway的配置類(GatewayConfiguration)中,注入一個實例限流時返回BlockRequestHandler

@Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(BlockRequestHandler myBlockRequestHandler) {
        // Register the block exception handler for Spring Cloud Gateway.

        //重定向bloack處理
        GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("http://www.baidu.com"));

        //自定義bloack處理
        //GatewayCallbackManager.setBlockHandler((com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler) myBlockRequestHandler);

        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
/**
 * 自定義的BlockRequestHandler
 *
 * @return
 */
@Bean(name = "myBlockRequestHandler")
public BlockRequestHandler myBlockRequestHandler() {
    BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
        @Override
        public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
            return ServerResponse.status(HttpStatus.BAD_GATEWAY)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue("服務器太熱了,它罷工了~" + throwable.getClass()));
        }
    };
    return blockRequestHandler;
}

Sentinel規則持久化

規則持久化到文件的步驟:

(1)配置依賴;

*

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-extension</artifactId>
</dependency>

(2)暫時不須要配置;

(3)寫代碼;

/**
 * 規則持久化
 */
public class FileDataSourceInit implements InitFunc {

    @Override
    public void init() throws Exception {
        //能夠根據須要指定規則文件的位置
        //String ruleDir = System.getProperty("user.home") + "/gateway/rules";
        String ruleDir = "E:\\software\\JAVA\\springcloud-alibaba\\package\\Sentinel-dashboard";

        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";

        this.mkdirIfNotExits(ruleDir);

        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);

        // 流控規則:可讀數據源
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 將可讀數據源註冊至FlowRuleManager
        // 這樣當規則文件發生變化時,就會更新規則到內存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        // 流控規則:可寫數據源
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 將可寫數據源註冊至transport模塊的WritableDataSourceRegistry中
        // 這樣收到控制檯推送的規則時,Sentinel會先更新到內存,而後將規則寫入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);


        // 降級規則:可讀數據源
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        // 降級規則:可寫數據源
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);


        // 熱點參數規則:可讀數據源
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        // 熱點參數規則:可寫數據源
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);


        // 系統規則:可讀數據源
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        // 系統規則:可寫數據源
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);


        // 受權規則:可讀數據源
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        // 受權規則:可寫數據源
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    }


    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );

    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );

    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

(4)配置SPI;

resources\META-INF\services\com.alibaba.csp.sentinel.init.InitFunc

放入第三步類的路徑(copy reference)

規則持久化到Nacos的步驟:

一、添加sentinel-datasource-nacos依賴;

*

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

二、application.properties配置持久化數據源;

spring:
  cloud:
    sentinel:
      eager: true
      transport:
        dashboard: localhost:8080
      datasource:
      #ncs1 爲配置Map中的key鍵,隨便取
        ncs1:
          nacos:
            server-addr: locahost:8848
            data-id: ${spring.application.name}.json
            group-id: DEFAULT_GROUP
            rule-type: flow
            data-type: json

三、在nacos配置中心配置流控規則(每一個route均可以配置逗號隔開):

[
 {
  "resource": "abc",
  "controlBehavior": 0,
  "count": 1.0,
  "grade": 1,
  "limitApp": "default",
  "strategy": 0
 }
]

Gateway內部流程源碼分析

img

(1)根據自動裝配spring-cloud-gateway-core.jar的spring.factories;

(2)GatewayClassPathWarningAutoConfiguration檢查前端控制器;

(3)網關自動配置GatewayAutoConfiguration;

(4)RoutePredicateHandlerMapping.getHandlerInternal(...)獲取Route;

(5)執行FilteringWebHandler


Gateway跨域CORS

咱們知道,傳統的Ajax請求只能獲取在同一個域名下的資源,可是HTML5規範中打破了這種限制,容許Ajax發起跨域的請求;(只是須要設置一下)

其實瀏覽器自己是能夠發起跨域請求的,好比你能夠連接一個另外一個域名下的圖片或者js,好比,可是javascript腳本是不能獲取這些另外一個域名下的資源內容的;

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing),它容許瀏覽器向跨域的另外一臺服務器發出XMLHttpRequest請求,從而克服了AJAX只能訪問同域名下的資源的限制;

這種CORS使用了一個額外的HTTP響應頭來賦予當前user-agent(瀏覽器)得到跨域資源的權限,這裏的跨域也就是Cross-Origin的概念,這裏的權限就是訪問另外一個域名下的資源權限;

CORS是如今HTML5標準中的一部分,在大部分現代瀏覽器中都有所支持,可能在某些老版本的瀏覽器不支持CORS,若是要兼容一些老的瀏覽器版本,則須要採用JSONP進行跨域請求;

同源與非同源的定義(跨域和不跨域)

若是 訪問協議、端口(若是指定了端口的話)、host都相同,則稱之爲同源(不跨域),不然爲非同源(跨域)
好比源連接: http://store.company.com/dir/page.html

URL 是否同源 緣由
http://store.company.com/dir2/other.html
http://store.company.com/dir/inner/another.html
https://store.company.com/secure.html 協議不一樣
http://store.company.com:81/dir/etc.html 端口不一樣
http://news.company.com/dir/other.html host不一樣

Spring Cloud Gateway解決跨域問題,只須要配置以下代碼便可:

*/****
\* *** *配置網關跨域**cors**請求支持**
\* **/**
*@Configuration
public class CorsConfig {
  @Bean
  public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedMethod("*");//是什麼請求方法(POST,GET)
    config.addAllowedOrigin("*");//來自哪一個域名的請求,*表明全部
    config.addAllowedHeader("*");//是什麼請求頭
    UrlBasedCorsConfigurationSource source = new 
UrlBasedCorsConfigurationSource(new PathPatternParser());
    source.registerCorsConfiguration("/**", config);
    return new CorsWebFilter(source);
  }
}
相關文章
相關標籤/搜索