• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Y-wee
博客园    首页    新随笔    联系   管理     

sentinel基本使用

sentinel基本使用

随着微服务的流行,服务和服务之间的稳定性变得越来越重要

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

本地下载安装并启动sentinel

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

官方文档:https://sentinelguard.io/zh-cn/docs/flow-control.html

下载后得到一个jar包,进入当前jar包所在目录启动cmd,通过命令启动jar包:

java -jar sentinel-dashboard-1.8.3.jar

浏览器访问:http://localhost:8080/#/login

进入登录页面,输入用户名和密码登录(默认用户名和密码都是sentinel)

sentinel监控服务

导入依赖

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

配置文件

spring:
  cloud:
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # Sentinel后台通信端口,默认8719,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

# 监控端口
management:
  endpoints:
    web:
      exposure:
        # 暴露所有端口
        include: '*'

随便写两个测试接口

package com.yl.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * sentinel测试接口
 *
 * @auther Y-wee
 */
@RestController
@Slf4j
@RequestMapping("/sentinel")
public class FlowLimitController {
    /**
     * 测试A
     *
     * @return testA
     */
    @GetMapping("/testA")
    public String testA() {
        return "testA";
    }

    /**
     * 测试B
     *
     * @return testB
     */
    @GetMapping("/testB")
    public String testB() {
        log.info(Thread.currentThread().getName() + "\t" + "...testB");
        return "testB";
    }

}

启动服务,发送请求,然后登录sentinel就可以看到相关监控信息

由于sentinel采用懒加载模式,只启动服务是看不到监控信息的,只有发送请求了sentinel才会产生监控信息

sentinel流量控制

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

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

阈值类型/单机阈值:

  • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
  • 线程数:当调用该api的线程数达到阈值的时候,进行限流

流控模式:

  • 直接:api达到限流条件时,直接限流
  • 关联:当关联的资源达到阈值时,就限流自己
  • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】

流控效果:

  • 快速失败:直接失败,抛异常
  • Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
  • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

@SentinelResource配置热点参数限流

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控

要使用热点参数限流功能,需要以下依赖(spring-cloud-starter-alibaba-sentinel依赖包含了以下依赖不需要额外导入):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>x.y.z</version>
</dependency>

然后为对应的资源配置热点参数限流规则,并在 entry (入口)的时候传入相应的参数,即可使热点参数限流生效,eg:

package com.yl.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * sentinel测试接口
 *
 * @auther Y-wee
 */
@RestController
@Slf4j
@RequestMapping("/sentinel")
public class FlowLimitController {

    /**
     * 测试热点参数限流
     * 注解@SentinelResource方式定义的资源,若注解作用的方法上有参数
     * Sentinel 会将它们作为参数传入 SphU.entry(res, args),从而可以用于热点规则判断
     * 注解@SentinelResource的value值为热点规则资源名,blockHandler指定自定义降级方法
     * 如果不指定降级方法,触发热点限流规则直接抛出异常
     *
     * @param p1 参数一
     * @param p2 参数二
     * @return 结果
     */
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2) {
        return "testHotKey";
    }

    /**
     * 降级方法
     * 注意:方法参数要加上BlockException
     *
     * @param p1
     * @param p2
     * @param exception
     * @return
     */
    public String deal_testHotKey(String p1, String p2, BlockException exception) {
        return "deal_testHotKey,o(╥﹏╥)o";  
    }

}

sentinel新增热点规则:请求包含第一个参数(参数值不变),且在1s内请求数量超过1时,触发降级方法

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

当有特殊情况,我们希望p1为某个特殊值时,限流值不一样则可以在高级选项中进行配置

@SentinelResource配置流量控制

package com.yl.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.yl.common.entity.CommonResult;
import com.yl.common.entity.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 注解@SentinelResource配置流量控制
 *
 * @auther Y-wee
 */
@RestController
@RequestMapping("/sentinel")
public class RateLimitController {
    /**
     * 测试@SentinelResource配置流量控制
     * value:流控规则资源名;blockHandler:降级方法(如果不配置触发流控规则则调用sentinel默认降级方法)
     *
     * @return 测试结果
     */
    @GetMapping("/resource")
    @SentinelResource(value = "resource",blockHandler ="handleException" )
    public CommonResult byResource() {
        return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
    }
}

注意:@SentinelResource不支持私有方法

以上配置降级方法跟接口方法在同一个类,不符合业务逻辑,需要将降级方法写在单独的类中隔离,实现步骤:

编写降级处理类自定义降级方法

package com.yl.sentinel.myhandler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.yl.common.entity.CommonResult;


/**
 * 自定以降级方法
 *
 * @auther Y-wee
 */
public class CustomerBlockHandler {
    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(4444, "按客戶自定义,global handlerException----1");
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(4444, "按客戶自定义,global handlerException----2");
    }
}

controller

package com.yl.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.yl.common.entity.CommonResult;
import com.yl.common.entity.Payment;
import com.yl.sentinel.myhandler.CustomerBlockHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 注解@SentinelResource配置流量控制
 *
 * @auther Y-wee
 */
@RestController
@RequestMapping("/sentinel")
public class RateLimitController {
    
    /**
     * 测试@SentinelResource隔离降级方法
     * blockHandlerClass:指定降级方法所在类
     *
     * @return
     */
    @GetMapping("/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,
            blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200, "按客戶自定义", new Payment(2020L, "serial003"));
    }
}

sentinel服务异常降级处理

@SentinelResource中blockHandler处理的是sentinel控制台配置的违规情况会调用降级方法,当有运行异常时,依旧会抛出异常(blockHandler主管配置出错,运行出错该走异常走异常),要处理运行时异常需要用到@SentinelResource中的fallback,总结:

  • blockHandler只负责sentinel控制台配置违规
  • fallback只负责业务异常

代码实战

package com.yl.sentinel.order.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.yl.common.entity.CommonResult;
import com.yl.common.entity.Payment;
import com.yl.sentinel.order.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @auther Y-wee
 */
@RestController
@Slf4j
@RequestMapping("/order")
public class CircleBreakerController {
    public static final String SERVICE_URL = "http://sentinel-payment";
    @Resource
    private RestTemplate restTemplate;

    /**
     * fallback指定服务异常时的降级方法
     * 如果blockHandler和fallback都配置了
     * 服务异常时调用fallback指定的降级方法,违反控流规则时调用blockHandler指定的降级方法
     *
     * @param id id
     * @return result
     */
    @RequestMapping("/fallback/{id}")
    @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler") 
    public CommonResult<Payment> fallback(@PathVariable Long id) {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/payment/get/" + id, CommonResult.class, id);

        if (id == 4) {
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
        } else if (result.getData() == null) {
            throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

    /**
     * 降级方法
     *
     * @param id
     * @param e
     * @return
     */
    public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容  " + e.getMessage(), payment);
    }
    
    /**
     * 降级方法
     *
     * @param id
     * @param blockException
     * @return
     */
    public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水: blockException  " + blockException.getMessage(), payment);
    }

}

扩展

/**
 * exceptionsToIgnore:顾名思义,当触发指定异常(IllegalArgumentException),不调用降级方法直接将异常抛出
 */
@SentinelResource(value = "fallback", 
            fallback = "handlerFallback", 
            blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})

如果需要sentinel结合feign使用,则要在配置文件开启sentinel对feign的支持:

# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true

sentinel持久化配置

如果没有配置sentinel持久化,服务重启,sentinel规则将消失,需要重启配置

将流控规则持久化进nacos保存,服务重启,sentinel流控规则持续有效,配置如下:

导入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

配置文件

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: sentinel
            groupId: DEFAULT_GROUP
            # 配置文件类型
            data-type: json
            rule-type: flow

nacos新建配置文件,Data ID:sentinel、Group:DEFAULT_GROUP、配置格式:JSON、配置内容如下:

[
    {
        // 资源名称
        "resource":"/sentinel/testA",
        // 来源应用
        "limitApp":"default",
        // 阈值类型,0-线程数,1-QPS
        "grade":1,
        // 单机阈值
        "count":1,
        // 流控模式,0-直接,1-关联,2-链路
        "strategy":0,
        // 流控效果,0-快速失败,1-Warm Up,2-排队等待
        "controlBehavior":0,
        // 是否集群
        "clusterMode":false
    }
]

nacos配置时把注释删除,防止报格式错误

配置完成,服务重启,流控规则不会消失

记得快乐
posted @ 2022-03-24 18:22  Y-wee  阅读(368)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3