阅读 1536
Spring Cloud Alibaba 实战(三)Sentinel篇

Spring Cloud Alibaba 实战(三)Sentinel篇

白菜Java自习室 涵盖核心知识

Spring Cloud Alibaba 实战(一)准备篇
Spring Cloud Alibaba 实战(二)Nacos篇
Spring Cloud Alibaba 实战(三)Sentinel篇
Spring Cloud Alibaba 实战(四)Oauth2篇
Spring Cloud Alibaba 实战(五)Zuul篇
Spring Cloud Alibaba 实战(六)RocketMQ篇
Spring Cloud Alibaba 实战(七)Seata篇
Spring Cloud Alibaba 实战(八)SkyWalking篇

项目 GitHub 地址:github.com/D2C-Cai/her…

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 文件

官方下载地址: github.com/alibaba/Sen…

  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. 打开刚才的“簇点链路”菜单,尝试对访问路径限流

  • 资源名:唯一名称,默认请求路径。
  • 针对来源: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 的强大好用是否已经征服你了呢?

Spring Cloud Alibaba 实战(一)准备篇
Spring Cloud Alibaba 实战(二)Nacos篇
Spring Cloud Alibaba 实战(三)Sentinel篇
Spring Cloud Alibaba 实战(四)Oauth2篇
Spring Cloud Alibaba 实战(五)Zuul篇
Spring Cloud Alibaba 实战(六)RocketMQ篇
Spring Cloud Alibaba 实战(七)Seata篇
Spring Cloud Alibaba 实战(八)SkyWalking篇

项目 GitHub 地址:github.com/D2C-Cai/her…

文章分类
后端
文章标签