Spring Cloud - Spring Cloud集成Sentinel实现服务限流

Sentinel概述

Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。

—— 引自Sentinel官网

Sentinel支持两种资源维度的限流:

  • Route维度
  • 自定义API维度

Route维度限流

  1. 引入Maven依赖
    com.alibaba.csp    sentinel-spring-cloud-gateway-adapter
  1. 网关配置类
@Configuration@AllArgsConstructorpublic class GatewayConfig {    private final List viewResolvers;    private final ServerCodecConfigurer serverCodecConfigurer;    @Bean    @Order(Ordered.HIGHEST_PRECEDENCE)    public GlobalFilter sentinelGatewayFilter() {        return new SentinelGatewayFilter();    }    // 配置限流异常处理器,即触发限流后的默认处理类    @Bean    @Order(Ordered.HIGHEST_PRECEDENCE)    public CustomSentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {        return new CustomSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);    }    @PostConstruct    public void doInit() {        initGatewayRules();    }    // 初始化限流规则    private void initGatewayRules() {        Set rules = new HashSet<>();        GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("jasmine-auth");        gatewayFlowRule.setCount(1D);        gatewayFlowRule.setIntervalSec(5L);        rules.add(gatewayFlowRule);        GatewayRuleManager.loadRules(rules);    }}
  1. 自定义全局限流异常处理器

在实际的应用中(笔者接触的项目大体是前后端分离的),我们通常以JSON的格式返回数据。我们自定义了一个异常处理器,当触发限流时返回JSON格式的异常数据。

public class CustomSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {    private final List viewResolvers;    List> messageWriters;    public CustomSentinelGatewayBlockExceptionHandler(List viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {        this.viewResolvers = viewResolvers;        this.messageWriters = serverCodecConfigurer.getWriters();    }    private Mono writeResponse(ServerResponse response, ServerWebExchange exchange) {        ServerHttpResponse serverHttpResponse = exchange.getResponse();        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");        Map map = new HashMap<>();        map.put("code", 500);        map.put("msg", "访问人数过多!");        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(new Gson().toJson(map).getBytes(StandardCharsets.UTF_8));        return serverHttpResponse.writeWith(Mono.just(dataBuffer));    }    // 省略无关代码}

自定义全局限流异常处理器的代码是直接从SentinelGatewayBlockExceptionHandler中复制过来,我们只修改writeResponse方法,该方法的作用是将限流的异常信息写回客户端。

配置类的主要功能如下:

  • 注入一个全局限流控制器
  • 注入自定义全局限流异常处理器
  • 初始化限流规则

网关限流规则(GatewayFlowRule)中提供了如下属性:

  • 资源名称(resource):可以是网关中的route名称或者用户自定义的API分组名称;
  • 资源模型(resourceMode):
  • 限流指标维度(grade):同限流规则的grade字段;
  • 限流阈值(count)
  • 统计时间窗口(intervalSec):单位是秒,默认1秒;
  • 流量整形的控制效果(controlBehavior)
  • 应对突发请求时额外允许的请求数目(burst)
  • 匀速排队模式下的最长排队时间(maxQueueingTimeoutMs):单位是毫秒,仅在匀速排除模式下生效;
  • 参数限流配置(paramItem)
    • 从请求中提取参数的策略(parseStrategy),目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意Header (PARAM_PARSE_STRATEGY_HEADER)和任意URL参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
    • 若提取策略选择Header模式或URL参数模式,则需要指定对应的Header名称或URL参数名称。
  1. 项目配置文件
server:  port: 9010spring:  profiles:    active: dev  application:    name: jasmine-sentinel  cloud:    nacos:      config:        file-extension: yaml        group: ${spring.profiles.active}        prefix: ${spring.application.name}        server-addr: 127.0.0.1:8848    gateway:      discovery:        locator:          enabled: true          # 是否使用service-id的小写,默认是大写          lower-case-service-id: true      routes:        - id: jasmine-auth          uri: lb://jasmine-auth          predicates:            - Path=/auth/**

关键配置说明:

  • uri: 配置的lb://表示从注册中心获取服务,后面的jasmine-auth表示目标服务在注册中心上的服务名。
  1. 测试

最后在浏览器中访问http://127.0.0.1:9010/auth/hello,多次刷新,当触发限流规则时看到的返回结果如下所示:

{"msg":"访问人数过多!","code":500}

自定义API分组限流

自定义API分组限流可以让多个Route共用一个限流规则。

自定义Api分组限流

// 自定义Api分组限流private void initCustomizedApis() {    Set definitions = new HashSet<>();    ApiDefinition apiDefinition = new ApiDefinition("jasmine-customized-api")        .setPredicateItems(new HashSet() {{            add(new ApiPathPredicateItem().setPattern("/auth/**"));            add(new ApiPathPredicateItem().setPattern("/log/**")                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));        }});    definitions.add(apiDefinition);    GatewayApiDefinitionManager.loadApiDefinitions(definitions);}

上述代码主要是将/auth/**和/log/**进行统一分组,并提供一个name=jasmine-customized-api,然后在初始化网关限流规则时,针对该name设置限流规则。同时,我们可以通过setMatchStrategy来设置不同path下的限流参数策略。

// 初始化限流规则private void initGatewayRules() {    Set rules = new HashSet<>();    GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("jasmine-customized-api");    gatewayFlowRule.setCount(1D);    gatewayFlowRule.setIntervalSec(5L);    gatewayFlowRule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);    rules.add(gatewayFlowRule);    GatewayRuleManager.loadRules(rules);}

initCustomizedApis()方法和initGatewayRules()方法一样,在初始化的时候调用。

@PostConstructpublic void doInit() {    initCustomizedApis();    initGatewayRules();}

这样,我们在浏览器中访问http://127.0.0.1:9010/auth/hello和http://127.0.0.1:9010/log/hello,多次刷新,都会触发限流规则,然后看到的返回结果如下所示:

{"msg":"访问人数过多!","code":500}

网关流控实现原理

图片引自官网。



发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章