SpringCloudAlibaba-Gateway

Gateway

Gateway简介

image-20210726105445118

image-20210725203850382

底层使用Netty框架,性能大于Zuul

image-20210725204038223

配置gateway模块,一般使用yaml格式:

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包冲突

在这里插入图片描述


Gateway核心概念

路由:网关的基本构建组成,它由ID,目标URI,谓词集合和过滤器集合定义,如果集合谓词为true,则匹配路由,否则不匹配;

谓词:这是Java 8函数谓词,输入类型是Spring Framework ServerWebExchange,可以匹配HTTP请求中的所有内容,例如请求头或参数;

过滤器:这些是使用特定工厂构造的Spring Framework GatewayFilter实例,可以在发送给下游请求之前或之后修改请求和响应;

下图从总体上概述了Spring Cloud Gateway的工作方式:

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路由谓词工厂采用两个参数,即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内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,可以自定义路由谓词工厂来实现特定的需求;

下面列举两个例子:

1、要求请求必须携带一个token,并且token值等于指定的值,才能访问;
2、要求某个服务的用户只允许在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是为了流控熔断降级,具体集成整合步骤如下:

1、添加依赖;

*

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

2、在gateway配置文件中添加sentinel控制台配置;

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

3、写代码,在spring容器中配置一个Sentinel的全局过滤器;

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

4、可以进行测试;

自定义网关限流返回信息

在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的步骤:

1、添加sentinel-datasource-nacos依赖;

*

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

2、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

3、在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.htmlhost不同

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);
  }
}

| 是 | |
| 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);
  }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值