Spring Cloud Alibaba 实战(三)Sentinel篇

1. Sentinel 简介

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

Sentinel 具有以下特征:

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

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

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

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

2. 使用 Docker 快速搭建 Sentinel 1.8

  1. 先从 github 上下载 sentinel-dashboard-1.8.0.jar 文件

官方下载地址:https://github.com/alibaba/Sentinel/releases

图片

  1. 在 linux 服务器上选择并建立目录

mkdir sentinel-docker
  1. 进入 sentinel-docker 目录,并把下载的 sentinel-dashboard-1.8.0.jar 放至该目录

cd sentinel-docker

图片

  1. 新建一个 Dockerfile 文件,内容如下:

FROM openjdk:8

ENV SENTINEL_HOME /opt/sentinel-dashboard

RUN mkdir -p ${SENTINEL_HOME}

COPY sentinel-dashboard-1.8.0.jar ${SENTINEL_HOME}

RUN chmod -R +x ${SENTINEL_HOME}/*jar

WORKDIR ${SENTINEL_HOME}

EXPOSE 8858

EXPOSE 8719

CMD java ${JAVA_OPS} -jar sentinel-dashboard-1.8.0.jar
  1. 新建一个 sentinel-dashboard.yaml 文件,内容如下:

version: '2'

services:
  sentinel-dashboard:
    image: sentinel-dashboard:1.8.0
    container_name: sentinel-dashboard
    restart: on-failure
    build:
      context: .
      dockerfile: Dockerfile
    ports:
    - "8858:8858"
    - "8719:8719"
    environment:
      - JAVA_OPS=-Dserver.port=8858
  1. 执行 sentinel-dashboard.yaml 脚本启动容器

docker-compose -f sentinel-dashboard.yaml up
  1. 登录 Sentinel 控制台,默认用户名为 sentinel,密码为 sentinel

http://(安装Sentinel机器的IP):8858

Sentinel 控制台里目前什么都没有,如何使用呢?这里我们先不急,先来配置 Spring Boot 项目客户端。

3. 在 Spring 项目中引入 Sentinel 客户端

  1. 添加 pom 文件依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  1. 在 application.yml 添加配置

server:
  port: 10801
  servlet:
    context-path: /api/member
    
spring:
  application:
    name: member-service
    
  cloud:
    sentinel:
      eager: true
      transport:
        port: 8719
        dashboard: (安装Sentinel机器的IP):8858
  1. 新建一个 Service 类添加一个方法,添加注解 @SentinelResource

@Service
public class MemberService {

    @SentinelResource(value = "sayHello", fallback = "sayHelloFail")
    public String sayHello() {
        return "Hello, Member! ";
    }

    public String sayHelloFail() {
        return "I am sorry, Member! ";
    }

}
  1. 新建一个 Controller 类来调用这个 Service 的方法

@RestController
@RequestMapping
public class HelloController {

    @Resource
    private MemberService memberService;

    @RequestMapping("/service")
    public String service() {
        return memberService.sayHello();
    }

}
  1. 启动 Spring 项目,此时再去控制台会发现项目列表中已经出现,实时监控暂时没数据

http://(安装Sentinel机器的IP):8858

图片

图片

注意:仔细的同学发现这里的端口并不是 8719,因为我机器上另外开了几个服务,配置同样是 8719 端口,Sentinel 会自动识别冲突,并且按照 (端口号+1) 的规则自动分配端口。

  1. 访问下 /api/member/service 这个请求,看看结果

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

Hello, Member! 
  1. 重复多访问几次 /api/member/service 这个请求,发现实时监控面板出现数据

图片

4. Sentinel 控制台配合项目实战

4.1. 尝试对访问路径限流

  1. 打开刚才的“簇点链路”菜单,尝试对访问路径限流:11

图片

图片

  • 资源名:唯一名称,默认请求路径。

  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。

阈值类型

  • QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。

  • 线程数:当调用该api的线程数达到阈值的时候,进行限流。

流控模式

  • 直接:api达到限流条件时,直接限流。

  • 关联:当关联的资源达到阈值时就限流自己。A接口与B接口关联,当B接口到达阈值,让A接口限流起到保护B接口的作用。例如支付接口与下单接口,当支付接口到达阈值,让下单接口限流,起到保护支付接口的作用。

  • 链路:只记录指定链路上的流量(指定资源入口资源进来的流量,如果达到阈值,就进行限流)。

流控效果

  • 快速失败:是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

  • Warm up:即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

  • 排队等候:会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

  1. 我设置的 QPS的值是1,直接api,快速失败,所以 1秒内去多次请求 /api/member/service

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

Blocked by Sentinel (flow limiting)

事实证明已经被 Sentinel 限制访问,直接快速失败了。

4.2. 尝试对服务资源限流

首先,为了防止多规则的干扰,将刚才设置的 访问路径限流规则 删去。

图片

  1. 打开刚才的“簇点链路”菜单,尝试对服务资源限流

图片

QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。

图片

  1. 我设置的 QPS的值是1,直接api,快速失败,所以 1秒内去多次调用 memberService.sayHello()

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

I am sorry, Member!

结果为什么没有报错?居然能正常返回?仔细的同学会发现都是我设置了降级方法 sayHelloFail() 的功劳

    @SentinelResource(value = "sayHello", fallback = "sayHelloFail")
    public String sayHello() {
        return "Hello, Member! ";
    }

    public String sayHelloFail() {
        return "I am sorry, Member! ";
    }

4.3. 尝试对异常服务熔断

  1. 打开刚才的“簇点链路”菜单,尝试对异常服务熔断

图片

熔断策略

  • 慢调用比例:选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

  • 异常比例:当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

  • 异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

图片

  1. 我们对代码进行一点小改造,并且重启项目:

    @RequestMapping("/service")
    public String service(String name) throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(400);
        return memberService.sayHello(name);
    }
  1. 我设置的 最大容忍响应时间是 200ms,比例是 50%,代码中强制延迟 400ms,也就是 100% 超时,并且设置了在单位时间内 5 次请求有 50% 是这种情况才会熔断,所以 1 秒内去超过 5 次请求 /api/member/service

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

Blocked by Sentinel (flow limiting)

事实证明已经被 Sentinel 限制访问,异常服务熔断了。

  1. 我设置的 熔断时间为 2 秒在 2 秒后再去请求 2 次 /api/member/service

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

Hello, Member!
Blocked by Sentinel (flow limiting)

结果为什么是这样?不是才请求 2 次吗?而且 2 秒后不是应该放行了吗?因为我代码中强制延迟 400ms,2 秒后的请求还是超时,第一次成功是因为需要统计数据

Sentinel 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

  1. 类似 4.2章节 的情形,如果我把代码中强制延迟 400ms 写在资源服务中

    @SentinelResource(value = "sayHello", fallback = "sayHelloFail")
    public String sayHello(String name) throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(400);
        return "Hello, Member! " + name;
    }

    public String sayHelloFail(String name) {
        return "I am sorry, Member! " + name;
    }

图片

一样的设置,一样的请求, 1 秒内去超过 5 次请求 /api/member/service

#### 请求测试
GET http://localhost:10801/api/member/service
Accept: */*
Cache-Control: no-cache

请求得到结果:

I am sorry, Member!

事实证明已经被 Sentinel 限制访问,异常服务虽然已经被熔断,但是设置了降级方法 sayHelloFail() 还是能正常返回结果

4.4. 尝试对热点参数限流

  • 热点就是经常访问的数据;

  • 比如商品接口的 QPS 限定的是 100,有一天要秒杀,带着秒杀商品 ID 的请求的 QPS 限制在 50,这样还能有 50 的 QPS 用来访问其他的商品;

  1. 我们先对代码进行一点小改造,加一个请求参数 name**,并且重启项目**:

@Service
public class MemberService {

    @SentinelResource(value = "sayHello", fallback = "sayHelloFail")
    public String sayHello(String name) {
        return "Hello, Member! " + name;
    }

    public String sayHelloFail(String name) {
        return "I am sorry, Member! " + name;
    }

}
@RestController
@RequestMapping
public class HelloController {

    @Resource
    private MemberService memberService;

    @RequestMapping("/service")
    public String service(String name) {
        return memberService.sayHello(name);
    }

}
  1. 打开的“热点规则”菜单,新增一个资源名为“sayHello”的热点规则

图片

  • 参数索引 设置 0,表示第一个请求参数

  • 单机阈值 设置 10,统计窗口时长 设置 1

  • 参数值 设置 字符串 baicai,限流阈值 设置 1

总的来说,就是同样的请求 /api/member/service?name=xxx, 当参数传 baicai 时,QPS 例外控制为 1,而参数传别的任何值,QPS 控制为 10。

  1. 尝试 1秒内去多次请求 /api/member/service?name=baicai

#### 请求测试
GET http://localhost:10801/api/member/service?name=baicai
Accept: */*
Cache-Control: no-cache

请求得到结果:

I am sorry, Member! baicai
  1. 尝试 1秒内去多次请求 /api/member/service?name=luobo

#### 请求测试
GET http://localhost:10801/api/member/service?name=luobo
Accept: */*
Cache-Control: no-cache

请求得到结果:

Hello, Member! luobo

事实证明当参数为 baicai 时,QPS 超过 1 就被限制访问,但是设置了降级方法 sayHelloFail() 还是能正常返回结果

4.5. 尝试黑白名单控制

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

图片

图片

测试结果和以上类似,这里就不做过多阐述了。看到这里,Sentinel 的强大好用是否已经征服你了呢?

猜你喜欢

转载自blog.csdn.net/adparking/article/details/119353014