服务降级—Sentinel

简介

1、什么是 Sentinel

是一个 SpringBoot项目。“哨兵”,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。它就是一个分布式系统的流量防卫兵。它相比 Hystrix 更加的强大。

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。

  • 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

2、下载使用

2.1、下载

官网下载:https://github.com/alibaba/Sentinel/releases

选择想要的版本,然后下载 sentinel-dashboard-1.8.4.jar。

2.2、使用

启动后会提供一个 web平台,localhost:8080,账号密码默认 sentinel,就开启监控页面了。

2.2.1、放入 idea 中使用

下载下来是一个 jar包,可以在项目下新建一个目录,然后把下载的 jar包放进去。

然后点击编辑配置,然后点 +,创建一个 JAR应用程序,然后为其命名,选择对应 jar 路径,填写完整对应的工作目录,然后在环境变量中修改其启动端口(默认 8080)。

2.2.2、在文件夹中使用

当然也可以直接去文件夹下 cmd,然后命令启动,如果是使用这种方法,写一个 bat 会方便:

java -Dserver.port=8888 -jar sentinel-dashboard-版本号.jar

3、流量控制

我们的服务不可能无限制地接受和处理客户端的请求,如果不加以限制,当发生高并发情况时,系统资源将很快被耗尽。

为了避免这种情况,我们就可以添加流量控制(也可以说是限流)。当一段时间内的流量到达一定的阈值的时候,新的请求将不再进行处理,这样不仅可以合理地应对高并发请求,同时也能在一定程度上保护服务器不受到外界的恶意攻击。

3.1、实现策略

  • 快速拒绝:既然不再接受新的请求,那么我们可以直接返回一个拒绝信息,告诉用户访问频率过高。

  • 预热:依然是基于方案一,但是由于某些情况下高并发请求是在某一时刻突然到来,我们可以缓慢地将阈值提高到指定阈值,形成一个缓冲保护。

  • 排队等待:不接受新的请求,但是也不直接拒绝,而是进队列先等一下,如果规定时间内能够执行,那么就执行,要是超时就算了。

3.2、判断是否超过流量阈值的算法

3.2.1、漏桶算法

3.2.1.1、思想

就像一个桶开了一个小孔,水流进桶中的速度肯定是远大于水流出桶的速度的,桶是有容量的,所以当桶的容量已满时,就装不下水了,这时就只有丢弃请求了。

3.2.2、令牌桶算法

3.2.2.1、思想

有点像信号量机制。现在有一个令牌桶,这个桶是专门存放令牌的,每隔一段时间就向桶中丢入一个令牌(速度由我们指定)。当新的请求到达时,将从桶中删除令牌(即该请求先去删除令牌才能放行,相当于请求拿到了令牌),接着请求就可以通过并给到服务,但是如果桶中的令牌数量不足,那么不会删除令牌,而是让此数据包等待。

3.2.3、固定时间窗口算法

3.2.3.1、思想

对某一个时间段内的请求进行统计和计数,比如在13:14到 13:15这一分钟内,请求量不能超过 100,也就是一分钟之内不能超过 100次请求。

但是因为时间是固定的,所以只要时间间隔够长,在临界点附近就会存在安全隐患。比如 13:14:59 瞬间来了 100个请求,然后 13:15:01 又瞬间来了 100个请求,那么就是 3秒内 200个请求,这就与我们的初衷背道而驰了。

3.2.4、滑动时间窗口算法

3.2.4.1、思想

相对于固定窗口算法,滑动时间窗口算法更加灵活,它会动态移动窗口,重新进行计算。时间间隔是固定的,但是临界点是根据请求到达的时间动态调整的。但是会消耗更多资源。

即从请求到的时刻计算前 1分钟内的流量计数,如果加起来超过了 100,那就不允许请求通过。

3.3、控制台实现流量控制

点击管理界面左侧的 “簇点链路”,然后就可以展示我们所有的 API接口,点击右侧的 “流控”按钮,然后选择并设置想要的效果就可以创建流量控制规则了。可以点击左侧“流控规则”去看到。

3.3.1、流控模式

  • 直接:只针对于当前 API接口

  • 关联:当同一个服务的其他接口超过阈值时,会导致当前接口被限流

  • 链路:更细粒度的限流,能够精确到具体的方法。即不局限于 API接口了,但是需要使用 @SentinelResource注解开启。选择链路后,可以指定一个入口资源,这个入口资源可以是路径的别名,比如 /user/{id},那入口资源可以写成 /user1/{id},访问此路径就相当于访问同一个 API接口,然后快速访问 /user/{id} 不会被限流,快速访问 /user1/{id} 会被限流。

3.3.1.1、简单实现方法限流

有该注解的方法就会被监控,即“埋点”,启动后在sentinel控制台中,通过指定的名字,可以对该名字对应的资源的访问进行控制。

被使用了 @SentinelResource注解的方法,会让 Sentinel控制台监控此方法,无论被谁执行都在监控范围内。这个注解可以使用在任何方法上。

这个注解还可以指定服务降级后使用的方法等等,有 blockHandler、fallback 等等参数。blockHandler 和 fallback 这俩个属性都可以用来配置一个指定的方法。blockHandler 是细粒度的配置, fallback 就相当于 Hystrix,会立即做降级。如果同时出现,以 fallback 为准。

blockHandler属性指定的方法,一般用于 sentinel控制台中设置的流量控制规则或服务降级规则生效后所调用的方法。

fallback属性指定的方法,这里表示当前方法调用后,如果调用失败了报错或者调用时间超时了,那么会自动调用 fallback 指定的方法。

spring:
  application:
    name: borrowservice
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858
      # 关闭 Context收敛,这样被监控的方法可以进行不同链路的单独控制
      web-context-unify: false
​
@Service
public class BorrowServiceImpl implements BorrowService{
  @Override
  // 指定的 value 是自定义名称,默认会将方法名作为名称,在控制台中显示
  @SentinelResource("getBorrow")
  public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
    List<Borrow> borrow = mapper.getBorrowsByUid(uid);
    User user = userClient.getUserById(uid);
    List<Book> bookList = borrow
          .stream()
          .map(b -> bookClient.getBookById(b.getBid()))
          .collect(Collectors.toList());
    return new UserBorrowDetail(user, bookList);
  }
}

3.3.2、系统规则

左侧菜单栏有那个“系统规则”,点击后可以设置系统的阈值类型。

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。

  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

4、限流和异常处理

之前在控制台中设置的流量控制,限流返回的界面是默认的,我们可以进行自定义。

4.1、设置限流页面

4.1.1、Controller 中的方法

@RequestMapping("/blocked")
JSONObject blocked(){
    JSONObject object = new JSONObject();
    object.put("code", 403);
    object.put("success", false);
    object.put("massage", "您的请求频率过快,请稍后再试!");
    return object;
}

4.1.2、application.yml

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858
      web-context-unify: false
      # 将刚刚编写的请求映射设定为限流页面
      block-page: /blocked

4.2、@SentinelResource方法限流

之前使用 @SentinelResource注解方法时,当某个方法被限流时,会直接在后台抛出异常,这是因为没有去指定处理异常的方法,就直接抛出了 BlockException异常。

blockHandler属性,指定在限流后抛出异常时,哪个方法来处理该异常,blockHandler 只能处理限流情况下抛出的异常,不能处理方法本身抛出的其他类型异常。

fallback属性,如果是方法本身抛出的其他类型异常,不是 BlockException异常,可以通过此参数进行指定对异常的替代方案。

若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

4.2.1、blockHandler

@Override
@SentinelResource(value = "getBorrow", blockHandler = "blocked")
public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
    // ...
}
​
// 替代方案,注意参数和返回值需保持一致,并且参数最后需额外添加一个 BlockException
public UserBorrowDetail blocked(int uid, BlockException e) {
    return new UserBorrowDetail(null, Collections.emptyList());
}

4.2.2、fallback

@RequestMapping("/test")
@SentinelResource(value = "test",
    // fallback指定出现异常时的替代方案
    fallback = "except",
    // 忽略的异常,也就是说这些异常出现时不使用替代方案
    exceptionsToIgnore = IOException.class)
String test(){
    throw new RuntimeException("HelloWorld!");
}
​
// 替代方法的返回值和参数必须和原方法一致,最后可以添加一个 Throwable 作为参数接受异常
String except(Throwable t){
    return t.getMessage();
}

4.3、热点参数限流

可以针对热点数据,即指定参数进行限流。

在 Sentinel控制台,左侧点击“热点规则”,点击“新增热点限流规则”,资源名就是 @SentinelResource注解中的名称,参数索引就是指定对哪个索引下标的参数进行限流,还可以设置参数例外项,设置特例。

@RequestMapping("/test")
@SentinelResource("test")
String findUserBorrows2(
      @RequestParam(value = "a", required = false) String  int a,
      @RequestParam(value = "b", required = false) String b,
      @RequestParam(value = "c",required = false) String c) {
    return "请求成功!a = "+a+", b = "+b+", c = "+c;
}

5、熔断器和服务降级

当服务链路上,A 调用 B,若 B 出现故障,那么 A 中的大量请求短时间内就得不到响应,就会堆积,这时候就需要进行服务隔离。

当下游服务因为某种原因变得不可用或响应过慢时,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务而是快速返回或是执行自己的替代方案,这便是服务降级。

5.1、服务隔离方案

5.1.1、线程池隔离

线程池隔离实际上就是对每个服务的远程调用单独开放线程池,比如服务A 要调用服务B,在服务A 中创建线程池,因为线程池提供的线程数量是固定的,那么即使在短时间内出现大量请求,由于没有线程可以进行分配,并且资源相互隔离,就不会导致资源耗尽了。

5.1.2、信号量隔离

信号量隔离是使用 Semaphore类实现的,思想是相同的,也是限定能够同时进行服务调用的线程数量,但是它相对于线程池隔离,开销会更小一些,使用效果同样优秀,也支持超时等。Sentinel 也正是采用的这种方案实现隔离的。 

5.2、熔断

5.2.1、熔断器工作过程

  • 关闭:熔断器不工作,所有请求正常工作。

  • 打开:熔断器工作,所有请求一律降级处理。

  • 半开:尝试进行一下进行正常流程,要是还不行继续保持打开状态,否则关闭。

5.2.2、控制台设置熔断

点击控制台左侧“熔断规则”,点击右上角的“新增熔断规则”,

5.2.2.1、熔断策略
  • 慢调用比例:如果出现那种半天都处理不完的调用,有可能就是服务出现故障,导致卡顿,这个选项是按照最大响应时间(RT)进行判定,如果一次请求的处理时间超过了指定的 RT,那么就被判定为慢调用,然后在一个统计时长内,如果请求数大于最小请求数,并且被判定为慢调用的请求比例已经超过阈值,那么将触发熔断。经过熔断时长之后,将会进入到半开状态进行试探。

  • 异常比例:这个与慢调用比例类似,不过这里判断的是出现异常的请求的次数。

  • 异常数:这个和“异常比例”的唯一区别就是,只要达到指定的异常数量,就熔断。

5.3、服务降级

Sentinel 的服务降级就是 @SentinelResource注解、blockHandler + 控制台的熔断规则。

6、远程服务调用支持 Sentinel

6.1、Feign 支持 Sentinel

6.1.1、实例1

6.1.1.1、配置文件
feign:
  sentinel:
    enabled: true
6.1.1.2、FeignClient接口和实现类
@FeignClient(value="user-service", fallback=UserClientFallback.class)
public interface Userclient {
  @RequestMapping("/user/{uid}")
  User getUserById(@PathVariable("uid") int uid);
}
​
​
​
@Compontent
public class UserClientFallback implements UserClient{
    @Override
    public User getUserById(int uid) {
        User user = new User();
        user.setName("我是替代方案");
        return user;
    }
}

6.2、传统的 RestTemplate 支持 Sentinel

@Bean
@LoadBalanced
@SentinelRestTemplate(blockHandler = "handleException",
    blockHandlerClass = ExceptionUtil.class,
    fallback = "fallback", fallbackClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
  return new RestTemplate();
}

7、简单使用

7.1、实例1

引入依赖,修改配置文件之后,就是将服务连接到 Sentinel控制台,然后让其进行监控。

7.1.1、pom.xml

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

7.1.2、application.yml

spring:
  application:
    name: userservice
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        # 添加监控页面地址即可
        dashboard: localhost:8858
# 开启 feign 对 sentinel 的支持
feign:
  sentinel:
    enabled: true

####

####

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值