gateway-使用

目录

环境准备 

nginx, 后面做高可用的时候使用

创建项目

路由规则(getway-server-0)

动态路由

断言(条件判断,是否走该路由id)

路径匹配

请求参数

请求方式

时间设置

远程地址

请求头参数设置

网关过滤器

PrefixPath 

RewritePath

StripPrefix

SetPath

AddRequestParameter

SetStatus

过滤器

自定义过滤器

全局过滤器

限流过滤器(网关限流)

限流算法

1.计数器算法

2.漏桶算法(Leaky Bucket)

3.令牌桶算法(Token Bucket)

redis令牌桶

3.1 添加依赖

3.2 设置限流规则

3.3 修改配置文件

Sentinel 限流(gateway-server-sentinel-0)

引入依赖

添加限流规则 

自定义异常处理器

限流分组

gateway高可用设置

源码地址


gateway 

官网: https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-starter

工作流程

环境准备 

nginx, 后面做高可用的时候使用

官网地址: http://nginx.org/en/download.html

创建项目

文章最后有源码地址,可以看具体的实现,

模块为  getway-server-0   和  gateway-server-sentinel-0

路由规则(getway-server-0)

动态路由

断言(条件判断,是否走该路由id)

路径匹配

http://172.28.14.55:9000/product/list

请求参数

http://172.28.14.55:9000/product/list?token=abc1

请求方式

http://172.28.14.55:9000/product/list?token=abc1

时间设置

http://172.28.14.55:9000/product/info?id=123&token=abc1

远程地址

规定远程地址为 172.28.14.52 则使用 localhost 的时候,无法访问

请求头参数设置

 

网关过滤器

PrefixPath 

- PrefixPath=/product #将 /1 重写为 /product/1

RewritePath

http://172.28.14.55:9000/api-gateway/product/info?id=123&token=abc1

- RewritePath=/api-gateway(?<segment>/?.*), $\{segment}  #将/api-gateway/product/info?id=123&token=abc1 重写为 /product/info?id=123&token=abc1

StripPrefix

http://172.28.14.55:9000/api-gateway/test/api/product/info?id=123&token=abc1

- RewritePath=/api-gateway(?<segment>/?.*), $\{segment}  #将/api-gateway/product/info?id=123&token=abc1 重写为 /product/info?id=123&token=abc1
- StripPrefix=2   #过滤掉前两个路径个数,将/api-gateway/test/api/product/info?id=123&token=abc1 第一个重写为 /test/api/product/info?id=123&token=abc1 第二个重写为 /product/info?id=123&token=abc1 ,

SetPath

http://172.28.14.55:9000/api-gateway/info?id=123&token=abc1

predicates:                    #断言(判断条件,相当于 zuul 中的 shouldFilter
  - Path=/product/**, /api-gateway/{segment}   #匹配对应的 url 的请求,将匹配到的请求追加在目标 url 之后,lb://service-provider/**
filters: #网关过滤器
  - SetPath=/product/{segment} #将 predicates - Path 中的{segment} 添加到 /product/{segment} 将 /api-gateway/info?id=123&token=abc1 重写为 /product/info?id=123&token=abc1

 

AddRequestParameter

predicates:                    #断言(判断条件,相当于 zuul 中的 shouldFilter
            - Path=/product/**, /api-gateway/{segment}   #匹配对应的 url 的请求,将匹配到的请求追加在目标 url 之后,lb://service-provider/**
            - Query=token, abc.         #匹配请求参数中包含token 并且参数值满足正则表达式 abc. 的请求
            - Method=GET                #匹配任意 GET 请求
            - After=2020-12-29T11:12:58.000+08:00[Asia/Shanghai]   #匹配北京时间 2020-12-29 11:12:58之后的时间,能够正常访问
            - RemoteAddr=172.28.14.55/0 #匹配远程地址请求是RemoteAddr 的请求, 0 表示子网掩码(表示所有网段)
          filters: #网关过滤器
            - SetPath=/product/{segment} #将 predicates - Path 中的{segment} 添加到 /product/{segment} 将 /api-gateway/info?id=123&token=abc1 重写为 /product/info?id=123&token=abc1
            - AddRequestParameter=flag, 10086 #在请求中添加 flag=10086

SetStatus

- SetStatus=NOT_FOUND    #任何情况下相应的 http 请求的状态都是404 或者请求的枚举 NOT_FOUND

接口正常的相应,但是返回的状态码是 404 

 

过滤器

自定义过滤器

创建过滤器

/**
 * 自定义网关过滤器
 * @author yangLongFei 2020-12-29-15:07
 */
public class CustomGatewayFilter implements GatewayFilter, Ordered {
    /**
     * 过滤器业务逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println(" !!!!!!!!!!!!!!自定义网关过滤器 !!!!!!!!!!!!!!");
        //继续向下执行
        return chain.filter(exchange);
    }

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     */
    @Override
    public int getOrder() {
        return 1;
    }
}

注册过滤器


/**
 * 网关路由配置类
 *
 * @author yangLongFei 2020-12-29-15:11
 */
@Configuration
public class GateWayRoutesConfiguration {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder locatorBuilder) {
        System.out.println(".......................自定义过滤器.......................");
        return locatorBuilder.routes().route(route ->
                route
                        //断言
                        .path("/product/**")
                        //目标 url, 路由到微服务地址
                        .uri("lb://service-provider")
                        //注册自定义网关过滤器
                        .filters(new CustomGatewayFilter())
                        // 路由唯一id
                        .id("product-service-custom-filter"))
                .build();
    }

}

全局过滤器

全局过滤器,使用 @Component 进行注册, 实现的是 GlobalFitler 接口 


/**
 * 自定义全局过滤器
 * @author yangLongFei 2020-12-29-15:51
 */
@Component
public class GlobalGatewayFilter implements GlobalFilter, Ordered {
    /**
     * 过滤器业务逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println(" ??????????????自定义全局过滤器 ??????????????");
        //继续向下执行
        return chain.filter(exchange);
    }

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     */
    @Override
    public int getOrder() {
        return -1;
    }

}

统一鉴权(使用的全局过滤器)


/**
 * 权限验证过滤器
 * @author yangLongFei 2020-12-29-15:59
 */
@Component
@Slf4j
public class AccessGlobalGatewayFilter implements GlobalFilter, Ordered {
    /**
     * 过滤器业务逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println(" 。。。。。。。。。。。。。。权限验证过滤器 。。。。。。。。。。。。。。");
        //获取请求参数
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //业务逻辑处理
        if (StringUtils.isEmpty(token)) {
            log.warn(" tokenn is null ........... ");
            ServerHttpResponse response = exchange.getResponse();
            //响应类型
            response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
            //相应转态码, 401 没有权限访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //相应内容
            String messge = "{\"message\": \"10000100001000000   "+ HttpStatus.UNAUTHORIZED.getReasonPhrase()+"\" }";
            DataBuffer wrap = response.bufferFactory().wrap(messge.getBytes());
            //请求结束,不执行后面的请求
            return response.writeWith(Mono.just(wrap));
        }
        // 使用token 进行身份验证
        log.info("token is ok ! ");
        log.info("value : " + token);

        //继续向下执行
        return chain.filter(exchange);
    }

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     */
    @Override
    public int getOrder() {
        return 3;
    }
}

通过上面的执行,依次经历

??????????????自定义全局过滤器 ??????????????
 !!!!!!!!!!!!!!自定义网关过滤器 !!!!!!!!!!!!!!
 。。。。。。。。。。。。。。权限验证过滤器 。。。。。。。。。。。。。。

但是最后,没有执行  配置文件中配置的  过滤器,因为执行了自定义的网关过滤器,

每个请求,除了全局过滤器外,只能匹配一个 非全局过滤器(通过测试时这样的)

路由唯一id 不一样的时候,默认先执行 自定义的网关过滤器

路由唯一id 一样的时候, 默认执行 自定义的网关过滤器

总的来说,先执行自定义网关过滤器,没有匹配成功执行,配置文件中的路由过滤器(自定义的网关过滤器可以和配置文件中的路由过滤器的 id 一样)

 

限流过滤器(网关限流)

限流算法

1.计数器算法

如果在 59秒之前一直没有请求,在59秒的时候发过来100个请求,然后在第二个 0 秒的时候又来了100 个请求,就造成服务器的压力过大 

2.漏桶算法(Leaky Bucket)

使用队列的机制实现

 会导致流量请求的堆积,不能及时的进行处理热点的请求

3.令牌桶算法(Token Bucket)

能够处理,突发的热点请求,也能以一定的速率去执行请求兼顾漏桶算法的优点

官方资料 : https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-requestratelimiter-gatewayfilter-factory

redis令牌桶

redis, 快速安装: https://blog.youkuaiyun.com/yang_zzu/article/details/105081615

docker, 安装: https://blog.youkuaiyun.com/yang_zzu/article/details/104469902

3.1 添加依赖

        <!-- 添加 spring data redis reactive 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <!-- commons-pool2 对象池依赖 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

3.2 设置限流规则

/**
 * 限流配置(redis 令牌桶)
 * @author yangLongFei 2020-12-30-19:56
 */
@Configuration
public class KeyResolverConfiguration {


    /**
     * 限流规则
     */
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    }
}

3.3 修改配置文件

filters: #网关过滤器
            - name: RequestRateLimiter    #限流过滤器
              args:
                redis-rate-limiter.replenishRate: 1   #令牌桶每秒填充速率
                redis-rate-limiter.burstCapacity: 3   #令牌桶总容量
#                key-resolver:  "#{@pathKeyResolver}"   #使用 SpEl 表达式按名称应用bean, 请求路径限流
#                key-resolver:  "#{@parameterKeyResolver}"   #使用 SpEl 表达式按名称应用bean, 请求参数限流
                key-resolver:  "#{@ipKeyResolver}"   #使用 SpEl 表达式按名称应用bean, ip限流

http://192.168.43.179:9000/product/info?id=123&token=abc1

多次访问接口后,会被限流,返回429状态码

Sentinel 限流(gateway-server-sentinel-0)

官网地址: https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

引入依赖

当前模块引入依赖

        <!-- sentinel getaway adapter 依赖 单独使用 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>

父依赖

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version>
    </properties>

    <!-- 使用 springcloud 进行版本的管理操作 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${com-alibaba-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

添加限流规则 

com.sys.yang.config.sentinel.GatewayConfiguration 都是在这个类上进行操作

查看官网写的demo,代码基本上是从上面拷贝的然后做了一点修改 

sentinel 官网 :  https://github.com/alibaba/Sentinel/wiki/

sentinel-demo :  https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-spring-cloud-gateway

/**
     * 网关限流规则
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("service-provider123")  // spring.cloud.gateway.routes.id ,即路由id 的值,
                .setCount(3)    //限流阈值
                .setIntervalSec(60)     //统计时间窗口,单位是秒,默认1秒
        );
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
    }

doInit , 将自己写的限流规则,通过 spring 进行初始化,写完方法后,将方法在这里进行调用加载 

    /**
     * spring 容器初始化的时候执行该方法
     */
    @PostConstruct
    public void doInit() {

        //限流分组
//        initCustomizedApis();
        // 加载网关阻塞规则
        initGatewayRules();
        //加载限流异常处理器
//        initBlockHandler();
    }

 注意:  GatewayFlowRule 的内容是  spring.cloud.gateway.routes.id ,即路由id 的值,

 

自定义异常处理器

/**
     * 自定义限流异常处理器
     */
    private void initBlockHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {

            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map<String, String> result = new HashMap<>();
                result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
                result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
                result.put("routeId", "service-provider123");
                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };

        //加载自定义限流异常处理器
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);

    }

限流分组

进行分组

    /**
     * 限流分组
     */
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        // product-api 组
        ApiDefinition api1 = new ApiDefinition("product-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/sentinelProduct/**"));
                    //匹配 /product 及其子路径的所有请求
                    add(new ApiPathPredicateItem().setPattern("/product/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        // order-api 组
        ApiDefinition api2 = new ApiDefinition("order-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    //匹配所有路径
                    add(new ApiPathPredicateItem().setPattern("/order/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api1);
        definitions.add(api2);
        // 加载限流分组
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

注意: 在 doInit 中调用方法,进行加载

对每个组,设置限流规则


    /**
     * 网关限流规则
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("service-provider123")  // spring.cloud.gateway.routes.id ,即路由id 的值,
                .setCount(6)    //限流阈值
                .setIntervalSec(60)     //统计时间窗口,单位是秒,默认1秒
        );
        // -------------------------限流分组 start ------------------------
        rules.add(new GatewayFlowRule("product-api")
                .setCount(3)
                .setIntervalSec(60));
        rules.add(new GatewayFlowRule("order-api")
                .setCount(3)
                .setIntervalSec(60));
        // -------------------------限流分组 end ------------------------
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
        
    }

yml 配置文件

      routes:
        - id: service-provider123            #路由id, 唯一
          uri: lb://service-provider      #目标url, lb:// 更具服务名称从注册中心获得服务请求地址
          predicates:                    #断言(判断条件,相当于 zuul 中的 shouldFilter
            - Path=/product/**, /sentinelProduct/**, /api-gateway/{segment}   #匹配对应的 url 的请求,将匹配到的请求追加在目标 url 之后,lb://service-provider/**
            - Query=token, abc.         #匹配请求参数中包含token 并且参数值满足正则表达式 abc. 的请求, abc 后面的点,匹配除换行符 \n 之外的任何单字符
            - Method=GET                #匹配任意 GET 请求
            - After=2020-12-29T11:12:58.000+08:00[Asia/Shanghai]   #匹配北京时间 2020-12-29 11:12:58之后的时间,能够正常访问
#          filters: #网关过滤器
#            - AddRequestParameter=flag, 10086 #在请求中添加 flag=10086 传递给接口
#            - SetStatus=NOT_FOUND    #任何情况下相应的 http 请求的状态都是404 或者请求的枚举 NOT_FOUND

        - id: service-consumer
          uri: lb://service-consumer
          predicates:                    #断言(判断条件,相当于 zuul 中的 shouldFilter
            - Path=/order/**          #匹配对应的 url 的请求,将匹配到的请求追加在目标 url 之后

product-api 组,是生产者 provider 服务中的,

service-provider123 路由id ,是对 provider 服务的路由规则

网关限流规则中,

首先对, service-provider123 ,设置60秒访问6次

又对, product-api 组设置为访问3次,

            组成员 /sentinelProduct/**  没有设置匹配策略  

            组成员 /product/**  设置了匹配策略 setMatchStrategy()

由于 product-api 中两个组成员是一组,所以访问的时候会出现:

情况1:

访问3次后,限流
http://172.28.14.55:5000/product/info?id=123&token=abc1

该请求能够正常访问,访问3次后限流
http://172.28.14.55:5000/sentinelProduct/list?token=abc4

情况2:

访问4次
http://172.28.14.55:5000/sentinelProduct/list?token=abc4

该请求能够正常访问,访问2次后,限流
http://172.28.14.55:5000/product/info?id=123&token=abc1

再次访问, 限流
http://172.28.14.55:5000/sentinelProduct/list?token=abc4

即:

service-provider123 匹配到的请求,只能访问6次,之后会被限流,组内的规则再对这 6 次请求进行进一步的划分

 

gateway高可用设置

gateway-server-sentinel-0 服务的配置文件,返回状态码的设置被取消了

getway-server-0 服务的配置文件,返回状态码被设置为404

nginx 配置反向代理,负载均衡

nginx启动

关闭的话,直接在任务管理器关闭

nginx 负载的时候,默认采用的是 轮询的方式,所以在请求的时候,会发现 返回码 会在 200 和 404 之间变换

http://172.28.14.55/product/info?id=123&token=abc1 

 

源码地址

https://github.com/YANG-sty/study

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值