接口限流

接口限流

Sentinel

由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务架构的轻量级高可用流量控制组件

提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性

sentinel

sentinel分为两部分:

  • sentinel-dashboard(控制面板):提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能
  • 客户端整合:每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。

部署

部署Sentinel Dashboard

下载地址:sentinel-dashboard

  • 运行jar包

    1
    2
    3
    4
    5
    6
    java -jar sentinel-dashboard-1.8.3.jar

    #后面可跟参数
    -Dserver.port=8080 #设置端口,默认8080
    -Dsentinel.dashboard.auth.username=sentinel #设置用户名, 默认sentinel
    -Dsentinel.dashboard.auth.password=123456 #设置密码, 默认sentinel
  • 访问localhost:8080来打开控制面板

客户端整合Sentinel
  • 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--sentinel-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.1.1.RELEASE</version>
    </dependency>
  • 配置Sentinel Dashboard的访问地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    spring:
    cloud:
    sentinel:
    transport:
    dashboard: localhost:8080 # 控制面板端口
    application:
    name: alibaba-sentinel-rate-limiting # 服务名
    server:
    port: 8001 # 服务端口
  • 编写一个访问的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.xw.controller;

    @Slf4j
    @RestController
    public class TestController {

    @GetMapping("/hello")
    public String hello() {
    return "hello";
    }
    }
  • 访问此接口和控制台

    sentinel例子

    sentinel控制台

限流

配置限流规则

对访问某个接口的所有请求进行限流

sentinel流控

sentinel新增流控规则

流控模式:

  • 直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认模式。

  • 关联:统计与当前相关的另一个资源,触发阈值时,对当前资源限流。

  • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

    链路模式中,是对不同来源的两个链路做监控。但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。

    我们需要关闭这种对SpringMVC的资源聚合

    1
    2
    3
    4
    spring:
    cloud:
    sentinel:
    web-context-unify: false # 关闭context整合

流控效果:

  • 快速失效:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
  • warm up:预热模式,对超出阈值的请求同样是拒绝,并抛出异常。但这种模式阈值会动态变化,从一个较小的值逐渐增加到最大值。
  • 排队等待:让所有请求按照先后次序排队执行,两个请求的间隔不能小于指定时长。对流量进行整形,将波动的qps整形成平滑的直线。
配置热点参数限流

粒度更细,对访问某个接口的指定参数进行限流

  • 热点参数限流对默认的SpringMVC是无效的,添加@SentinelResource注解开启热点限流

    1
    2
    3
    4
    5
    6
    @SentinelResource("hot")    //设置资源名
    @GetMapping("/hot")
    public String hot(@RequestParam String id) {
    System.out.println("热点参数");
    return "success";
    }
  • 通过控制面板设置

    热点规则1

    热点规则2

隔离

对访问某个接口的线程进行限制

FeignClient整合Sentinel

将Feign与Sentinel整合,在Feign里面实现线程隔离和服务熔断。

SpringCloud版本为<spring-cloud.version>Hoxton.SR8</spring-cloud.version>

循环依赖可能是版本问题

Feign整合Sentinel的步骤:

  • 在application.yml中配置:feign.sentienl.enable=true
  • 给FeignClient编写FallbackFactory并注册为Bean
  • 将FallbackFactory配置到FeignClient
  • 在用到feign的类的配置中开启sentinel功能

    1
    2
    3
    feign:
    sentinel:
    enabled: true # 开启feign对sentinel的支持
  • 编写失败降级逻辑

    业务失败后,不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。

    给FeignClient编写失败后的降级逻辑

    ①方式一:FallbackClass,无法对远程调用的异常做处理

    ②方式二:FallbackFactory,可以对远程调用的异常做处理

    在feign-api包中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package cn.itcast.client.fallback;

    @Slf4j
    public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
    return new UserClient() {
    @Override
    public User findById(Long id) {
    log.error("查询用户异常");
    return new User();
    }
    };
    }
    }

  • 将UserClientFallbackFactory注册为一个bean

    1
    2
    3
    4
    5
    6
    7
    public class DefaultFeignConfiguration {

    @Bean
    public UserClientFallbackFactory userClientFallbackFactory() {
    return new UserClientFallbackFactory();
    }
    }
  • 在用到feign的类上记得在启动类上设置默认配置类

    1
    @EnableFeignClients(clients = {UserClient.class},defaultConfiguration = DefaultFeignConfiguration.class)
  • 在feing-api项目中的UserClient接口中使用UserClientFallbackFactory

    1
    2
    3
    4
    5
    6
    7
    8
    package cn.itcast.client;

    //注意是fallbackFactory不是fallback
    @FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
    public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
    }
  • 重启服务

线程隔离

线程隔离的两种手段

  • 信号量隔离

  • 线程池隔离

信号量隔离的特点

  • 基于计数器模式,简单,开销小,适合高扇出的场景

线程池隔离的特点

  • 基于线程池模式,有额外开销,但隔离控制更强
配置隔离规则

配置隔离规则1

配置隔离规则2

熔断

熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

熔断策略:

  • 慢调用:当响应时间大于设定的值(RT)时,为一次慢调用。若慢调用的次数大于设定的阈值,请求数量超过设定的最小数量,则会触发熔断
  • 异常比例:单位时间里出现异常的比例大于设置的比例,则会触发熔断
  • 异常数: 单位时间里出现异常的比例大于设置的数值,则会触发熔断

配置慢调用

配置慢调用1

配置慢调用2

配置异常比例/异常数

配置异常比例和异常数

授权

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。

只对有授权的请求提供服务

默认情况下,sentinel对一切请求的来源都被认为是一样的值default。

  • 自定义请求来源解析器

    配置授权规则时会根据解析器的返回值来判断请求来源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package cn.itcast.order.sentinel;

    /**
    * 自定义来源解析器
    */
    @Component
    public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
    //从请求头获取来源信息
    String origin = request.getHeader("origin");
    //请求头中没有"origin",则默认值为"blank"
    if (StringUtil.isEmpty(origin)) {
    origin = "blank";
    }

    return origin;
    }
    }
  • 通过配置,给网关添加过滤器

    通过网关的请求都会被加上origin的请求头,值为”gateway”

    1
    2
    3
    4
    5
    spring:
    cloud:
    gateway:
    default-filters:
    - AddRequestHeader=origin,gateway
  • 配置授权规则

    配置授权规则1

    配置授权规则2

自定义异常结果

发生限流、降级、授权拦截时,都会抛出异常到调用方,异常结果都是flow limmiting(限流)

异常种类

异常 说明
FlowException 限流异常
ParamFlowException 热点参数限流的异常
DegradeException 降级异常
AuthorityException 授权规则异常
SystemBlockException 系统规则异常

通过自定义堵塞异常处理器来自定义返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cn.itcast.order.sentinel;

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知错误"; //异常信息
int status = 429; //状态码

if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = 401;
}

response.setContentType("application/json;charset=utf-8");
response.setStatus(status);
response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
}
}

接口限流
http://xwww12.github.io/2022/09/18/微服务/接口限流/
作者
xw
发布于
2022年9月18日
许可协议