Gateway网关限流

网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们本次采用Sentinel组件来实现网关的限流。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进行限流。

从Sentinel1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流:

  • route维度:即在Spring配置文件中配置的路由条目,资源名为对应的routeId
  • 自定义API维度:用户可以利用Sentinel提供的API来自定义一些API分组

目录

1 route维度限流

1.1 导入依赖

1.2 配置路由信息

1.3 编写配置类

1.4 测试

2 自定义API分组维度 实现限流


1 route维度限流

1.1 导入依赖

        
        
            com.alibaba.csp
            sentinel-spring-cloud-gateway-adapter
        

1.2 配置路由信息

在application.yml中,添加两个路由信息,分别转发到两个微服务中。实现对id=product_route的路由进行限流。

server:
  port: 7000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848   # nacos服务端地址
    gateway:
      discovery:
        locator:
          enabled: true # 启用探测器,让gateway可以发现nacos中的微服务
      routes: # 路由数组(路由:就是当指定请求满足什么条件的时候,转发到哪个微服务)
        - id: product_route  # 当前路由的标识,要求唯一。默认uuid
          uri: lb://service-product # lb指的是负载均衡(load balancing),service-product是nacos中微服务的名称
          order: 1  # 路由的优先级,数字越小级别越高
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters:  # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 # 转发之前去掉1层路径
        - id: order_route  # 当前路由的标识,要求唯一。默认uuid
          uri: lb://service-order # lb指的是负载均衡(load balancing),service-order是nacos中微服务的名称
          order: 1  # 路由的优先级,数字越小级别越高
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/order-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 # 转发之前去掉1层路径

1.3 编写配置类

基于Sentinel的Gateway限流是通过其提供的Filter来完成的,使用时只需注入对应的SentinelGatewayFilter实例以及SentinelGatewayBlockExceptionHandler实例即可。

package cn.jack.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

@Configuration
public class GatewayConfiguration {

    private final List viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 配置初始化的限流參數
     */
    @PostConstruct
    public void initGatewayRules() {
        Set rules = new HashSet();
        rules.add(
                new GatewayFlowRule("product_route")    // 资源名称,对应路由id
                    .setCount(1)        // 限流阈值
                    .setIntervalSec(5)   // 统计时间窗口(熔断时间),单位:秒。默认是1秒
        );

        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 自定义限流异常页面,限流返回的信息
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", 0);
                map.put("message", "接口被限流了");

                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

1.4 测试

通过网关访问微服务service-product,一秒内多次访问http://localhost:7000/product-serv/product/2就可以看到限流起作用了。

Gateway网关限流_第1张图片

而另一条路由则没有影响:

Gateway网关限流_第2张图片

2 自定义API分组维度 实现限流

自定义API分组是一种更细粒度的限流规则定义。它可以对匹配到的接口进行限流。

自定义API分组配置类完整代码如下:

package cn.jack.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

@Configuration
public class GatewayConfiguration2 {

    private final List viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration2(ObjectProvider> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 配置初始化的限流參數
     */
    @PostConstruct
    public void initGatewayRules() {
        Set rules = new HashSet();
        rules.add(
                new GatewayFlowRule("product_api_001")    // 资源名称,可以是sentinel的API定义的分组
                    .setCount(1)        // 限流阈值
                    .setIntervalSec(5)   // 统计时间窗口(即时间段内达到阈值实行限流),单位:秒。默认是1秒
        );

        rules.add(
                new GatewayFlowRule("product_api_002")    // 资源名称,可以是sentinel的API定义的分组
                        .setCount(1)        // 限流阈值
                        .setIntervalSec(5)   // 统计时间窗口(即时间段内达到阈值实行限流),单位:秒。默认是1秒
        );

        GatewayRuleManager.loadRules(rules);
    }

    // 自定义API分组
    @PostConstruct
    private void initCustomizedApis() {
        Set definitions = new HashSet<>();

        Set predicateItemSet1 = new HashSet();
        predicateItemSet1.add(new ApiPathPredicateItem()
                .setPattern("/product-serv/product/api001/**")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); // 前缀匹配
        ApiDefinition api1 = new ApiDefinition("product_api_001")
                .setPredicateItems(predicateItemSet1);

        Set predicateItemSet2 = new HashSet();
        predicateItemSet2.add(new ApiPathPredicateItem()
                .setPattern("/product-serv/product/api002/demo1")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));  // 精确匹配
        ApiDefinition api2 = new ApiDefinition("product_api_002")
                .setPredicateItems(predicateItemSet2);

        definitions.add(api1);
        definitions.add(api2);

        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /**
     * 自定义限流异常页面,限流返回的信息
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", 0);
                map.put("message", "接口被限流了");

                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

微服务service-product的测试接口:

package cn.jack.controller;

import cn.jack.domain.Product;
import cn.jack.service.ProductService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class ProductController {

    @RequestMapping("/product/api001/demo1")
    public String demo1() {
        return "demo";
    }

    @RequestMapping("/product/api001/demo2")
    public String demo2() {
        return "demo2";
    }

    @RequestMapping("/product/api002/demo1")
    public String demo3() {
        return "demo3";
    }

    @RequestMapping("/product/api002/demo2")
    public String demo4() {
        return "demo4";
    }
}

启动进行测试,会发现api001下的两个接口被限流了,而api002只有demo1接口被限流。

Gateway网关限流_第3张图片

Gateway网关限流_第4张图片

 

 

 

你可能感兴趣的:(微服务,gateway,网关限流)