【2023最新】SpringBoot3.x SpringCloudAlibaba2022.0.x 集成 Sentinel 实现限流/熔断

本教程以sentinel-dashboard-1.8.6.jar为例,nacos版本为2.2.1

一、获取控制台

Sentinel控制台jar包:Releases · alibaba/Sentinel · GitHub

Sentinel控制台源码:GitHub - alibaba/Sentinel

二、启动控制台

Linux下后台静默启动方式如下:

nohup java -server -Xms256m -Xmx256m -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar --add-exports=java.base/sun.net.util=ALL-UNNAMED sentinel-dashboard-1.8.6.jar &

Docker下启动方式如下:

1、下载sentinel-dashboard-1.8.6.jar

2、编写Dockerfile文件

# this is sentinel-dashboard dockerfile
# version 1.0
# 基础镜像
FROM eclipse-temurin:17-jre-focal
# 维护人
MAINTAINER luckykuang<luckykuang@foxmail.com>
#jar
COPY ./sentinel-dashboard-1.8.6.jar /home/pontus.fan/sentinel-dashboard.jar
# 端口
EXPOSE 8109
# 执行命令启动jar
ENTRYPOINT ["java","-jar","/home/pontus.fan/sentinel-dashboard.jar"]
# 时区
ENV TIME_ZONE=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone

3、编写docker-compose.yml文件

version: '3.3'
services:
  sentinel:
    build: ./
    image: senfel/sentinel-dashboard:1.8.6
    container_name: sentinel-dashboard
    ports:
      - 8109:8109
    environment:
      JVM_OPTS: "-server -Xmx512M -Xms512M -XX:MaxMetaspaceSize=256M -XX:CompressedClassSpaceSize=50M -XX:ReservedCodeCacheSize=240M -XX:MaxDirectMemorySize=400M"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "1"
    volumes:
      - "/home/pontus.fan/sentinel/logs:/root/logs"
      - "/home/pontus.fan/sentinel/logs:/app-logs"
    command: [
      "--server.port=8109",
      "--add-exports=java.base/sun.net.util=ALL-UNNAMED",
      "--logging.file.path=/app-logs"
    ]

4、运行sentinel-dashboard

docker-compose up -d --build

Windows不想后台静默启动,将首尾的nohup&去掉即可

具体的启动参数介绍:

  • -Dserver.port=8080 控制台端口,sentinel控制台是一个spring boot程序。客户端配置文件需要填对应的配置,如:spring.cloud.sentinel.transport.dashboard=localhost:8080
  • -Dcsp.sentinel.dashboard.server=localhost:8080 控制台的地址,指定控制台后客户端会自动向该地址发送心跳包。
  • -Dproject.name=sentinel-dashboard 指定Sentinel控制台程序的名称
  • -Dcsp.sentinel.api.port=8719 可选项,客户端提供给Dashboard访问或者查看Sentinel的运行访问的参数,默认8719,如果冲突会默认递增
  • --add-exports=java.base/sun.net.util=ALL-UNNAMED 如果是jdk17及以上版本,必须在启动参数上添加

三、访问控制台

浏览器输入网址:http://127.0.0.1:8080/#/login

默认登陆账户:sentinel/sentinel

四、微服务集成

需要引入的依赖如下:


<!-- openfeign 用于远程调用 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!-- loadbalancer 用于负载均衡 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

<!-- sentinel 用于限流/熔断 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- sentinel持久化到nacos 不持久每次重启都会清除规则 -->
<!-- 2022.0.0.0-RC2版本持久化无法使用,等后续更新 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</dependency>

<!-- nacos 配置中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!-- nacos 注册中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

限流操作

controller类的方法接口上添加@SentinelResource注解

关于@SentinelResource 注解,有以下的属性:

  • value:资源名称,必需项(不能为空)
  • entryType:entry类型,可选项(默认为 EntryType.OUT)
  • blockHandler/blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项
  • fallback/fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。

更多使用可参考官方文档:注解支持 · alibaba/Sentinel Wiki · GitHub

具体代码如下:

package com.luckykuang.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.luckykuang.sentinel.feign.UserClient;
import com.luckykuang.sentinel.handler.UserHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author luckykuang
 * @date 2023/7/17 11:24
 */
@Slf4j
@RestController
@RequestMapping("api/v1/userVo")
public class UserController {

    @Resource
    private UserClient userClient;

    @GetMapping("getUser")
    @SentinelResource(value = "getUser",blockHandler = "getUserBlockHandler")
    public String getUser(String name){
        if(name.contains("y")){
            log.info("client getUser exception name:{}",name);
            throw new RuntimeException("client getUser exception");
        }
        log.info("client getUser name:{}",name);
        return userClient.getUser(name);
    }
    /**
     * 限流/熔断降级:根据sentinel配置的阈值,超过即触发
     */
    public String getUserBlockHandler(String name, BlockException blockException){
        log.warn("限流/熔断业务处理"+"-"+name,blockException);
        return "限流/熔断业务处理"+"-"+name;
    }
}

熔断操作

具体代码如下:

package com.luckykuang.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.luckykuang.sentinel.feign.UserClient;
import com.luckykuang.sentinel.handler.UserHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author luckykuang
 * @date 2023/7/17 11:24
 */
@Slf4j
@RestController
@RequestMapping("api/v1/userVo")
public class UserController {

    @Resource
    private UserClient userClient;

    @GetMapping("getUser")
    @SentinelResource(value = "getUser",fallback = "getUserHandlerFallback")
    public String getUser(String name){
        if(name.contains("y")){
            log.info("client getUser exception name:{}",name);
            throw new RuntimeException("client getUser exception");
        }
        log.info("client getUser name:{}",name);
        return userClient.getUser(name);
    }
    /**
     * 异常处理:接口调用异常即触发
     */
    public String getUserHandlerFallback(String name,Throwable throwable){
        log.error("异常业务处理"+"-"+name,throwable);
        return "异常业务处理"+"-"+name;
    }
}

限流/熔断

限流和熔断都写,优化后的代码如下:

控制层代码UserController.java

package com.luckykuang.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.luckykuang.sentinel.feign.UserClient;
import com.luckykuang.sentinel.handler.UserHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author luckykuang
 * @date 2023/7/17 11:24
 */
@Slf4j
@RestController
@RequestMapping("api/v1/userVo")
public class UserController {

    @Resource
    private UserClient userClient;

    @GetMapping("getUser")
    @SentinelResource(value = "getUser",
            blockHandler = "getUserBlockHandler",blockHandlerClass = UserHandler.class,
            fallback = "getUserHandlerFallback",fallbackClass = UserHandler.class)
    public String getUser(String name){
        if(name.contains("y")){
            log.info("client getUser exception name:{}",name);
            throw new RuntimeException("client getUser exception");
        }
        log.info("client getUser name:{}",name);
        return userClient.getUser(name);
    }
}

抽离代码UserHandler.java

package com.luckykuang.sentinel.handler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * @author luckykuang
 * @date 2023/7/17 18:14
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserHandler {

    /**
     * 限流/熔断降级:根据sentinel配置的阈值,超过即触发
     */
    public static String getUserBlockHandler(String name, BlockException blockException){
        log.warn("限流/熔断业务处理"+"-"+name,blockException);
        return "限流/熔断业务处理"+"-"+name;
    }

    /**
     * 异常处理:接口调用异常即触发
     */
    public static String getUserHandlerFallback(String name,Throwable throwable){
        log.error("异常业务处理"+"-"+name,throwable);
        return "异常业务处理"+"-"+name;
    }
}

五、测试

通过Postman工具,测试接口

1、限流测试

设置限流阈值:

参数说明:

  • resource:资源名,即限流规则的作用对象
  • limitApp:流控针对的调用来源,若为default 则不区分调用来源
  • grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
  • count:限流阈值
  • strategy:调用关系限流策略(0代表直接、1代表关联、2代表链路)
  • controlBehavior:流量控制效果(0代表直接拒绝、1代表Warm Up、2代表匀速排队)
  • clusterMode:是否为集群模式 true-是 false-否

请求接口:http://localhost:9010/api/v1/userVo/getUser?name=x

测试结果

  • 规则配置在/api/v1/userVo/getUser接口上,请求超过设定的阈值,就会返回Blocked by Sentinel (flow limiting)
  • 规则配置在getUser接口上,请求超过设定的阈值,就会返回限流/熔断业务处理-x,进入限流处理业务,这里的getUser是注解@SentinelResource上的value值,该值一定要唯一

2、熔断测试

慢调用比例配置:

参数说明:

  • 最大RT:即接口的最大响应时间,单位:毫秒,当接口实际响应时间超过该设定的值时,sentinel会认为此次请求是一个慢调用请求
  • 比例阈值:取值范围0.1~1.0,在统计时长区间内,当sentinel统计到的慢调用次数占总的调用次数的比例,达到此阈值时,触发熔断。
  • 熔断时长:单位(秒),首次触发熔断,多长时间后再次尝试请求服务。如果首次熔断后,没有达到设定的熔断时长,再次请求时,不会直接调用服务,而是直接抛出异常,或者走blockHandler指定的异常处理逻辑。如果达到设定的熔断时长之后,再次请求服务时,如果sentinel发现请求耗时还是无法满足最大RT设定值时,会继续熔断,直到请求耗时满足最大RT设定值时恢复正常。
  • 最小请求数:统计时长区间内,需要采集的最小请求样本数,如果统计时长区间内,所有请求都达到了最大RT,但是总的请求数没有达到设定的最小请求数,那么也不会触发熔断
  • 统计时长:单位(毫秒)

测试结果

  • 达到慢比例熔断策略,也就是接口慢请求达到设置比例时,即会触发blockHandler处理

异常比例配置:

参数说明:

  • 比例阈值:取值区间0.1~1.0,统计时长区间内,如果发生异常的请求数占总的请求数的比例达到该阈值后,sentinel会触发熔断机制

测试结果

  • 达到异常比例熔断策略,也就是接口请求出现异常比例达到设置比例时,即会触发blockHandler处理

异常数配置:

参数说明:

  • 异常数:统计时长区间内,如果发生异常的请求数达到该阈值时,sentinel触发熔断机制

测试结果

  • 达到异常数熔断策略,也就是接口请求出现异常次数达到设置异常数量时,即会触发blockHandler处理

Tips

  1. 服务在哪里启动,sentinel控制台就在哪里启动,它只是一个查看的控制台,不影响已持久化到Nacos的配置

  2. Nacos2.2.1安装教程请看另一篇教程

Sentinel 限流/熔断案例传送门:GitHub

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值