Sentinel使用

Sentinel 介绍

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

官网地址

Sentinel 基本概念

在这里插入图片描述

服务雪崩

在微服务调用链路中,因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩效应。
导致雪崩效应的最根本原因是:大量请求线程同步等待造成的资源耗尽

解决方案
超时机制

设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止地等待。
在不做任何处理的情况下,服务提供者不可用会导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。但是这种解决方案只是缓解了雪崩问题,并不能解决雪崩问题。

服务限流

限制业务访问的QPS,避免服务因为流量的突增而故障。这种是从预防层面来解决雪崩问题。

舱壁隔离(资源隔离)

资源隔离分为进程隔离,线程隔离和信号量隔离。隔离机制的本质就是将服务调用的粒度划分的更小,以此来减少服务生产崩溃而对服务调用带来的影响,避免服务雪崩现象产生。

比如限定每个业务能使用的线程数,避免耗尽整个线程池的资源。它是通过划分线程池的线程,让每个业务最多使用n个线程,进而提高容灾能力。但是这种模式会导致一定的资源浪费,比如,服务C已经宕机,但每次还是会尝试让服务A去向服务C发送请求。

服务熔断降级

这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾。放到我们的系统中,如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

当依赖的服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源。比如我们设置了超时时间为3s,如果短时间内有大量请求在3s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个依赖了,这个时候就应该使用断路器避免资源浪费。

有服务熔断,必然要有服务降级。

所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个值。

Sentinel or Hystrix对比

Hystrix 的关注点在于以隔离和熔断为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。

而 Sentinel 的侧重点在于:

  • 多样化的流量控制
  • 熔断降级
  • 系统负载保护
  • 实时监控和控制台(感觉这个才是最关键的,可以不用在代码中去写规则代码,而是页面直接编辑)

Sentinel基本使用

书写代码控制保护的资源

  • pom依赖
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>
  • Controller
@Controller
@RequestMapping(value = "/sentinel/test")
public class SentinelController {

    private final String RESOURCE_NAME = "HelloWorld";

    @GetMapping(value = "/test1")
    @ResponseBody
    public String Test1(){
        try (Entry entry = SphU.entry(RESOURCE_NAME)) {
            // 被保护的逻辑
            System.out.println("hello world");
            return "正常返回";
        } catch (BlockException ex) {
            // 处理被流控的逻辑
            System.out.println("blocked!");
            return "被流控了";
        }
    }

    @PostConstruct
    //创建流控规则,这里以QPS每秒不能超过2为例
    //还可以根据线程数流控,可以去RuleConstant查看
    private static void initFlowRules(){
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource(RESOURCE_NAME);	//此规则用于RESOURCE_NAME这个资源
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // Set limit QPS to 20.
        rule.setCount(2);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

以上是基本使用,但这种写法,有很大缺点;

  • 业务侵入性很强,需要在controller中写入非业务代码.
  • 配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则

切面控制保护的资源

Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。

注意:注解方式埋点不支持 private 方法。

@SentinelResource 注解包含以下属性:

  • value: 资源名称,必需项(不能为空)
  • blockHandler: 定义当资源内部发生了BlockException应该进入的方法(捕获的是Sentinel定义的BlockEcxeption异常)。如果同时配置了blockHandler和fallback,出现BlockException时将进入BlockHandler方法中处理
  • fallback: 定义的是资源内部发生了Throwable应该进入的方法。默认处理所有的异常,如果我们不配置blockHandler,其抛出BlockEcxeption也将会进入fallback方法中
  • exceptionsToIgnore:配置fallback可以忽略的异常

源码入口:com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect

  • pom
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.6</version>
</dependency>
  • 配置切面支持
@Configuration
public class SentinelAspectConfiguration {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}
  • Controller中编写逻辑

@SentinelResource(value = "helloWorld",blockHandler = "handleException",
    fallback = "fallbackException")
@RequestMapping("/test")
public String test(String s1,String s2) {
    int i = 1 / 0;
    return "helloworld";
}

// Block 异常处理函数,参数比保护的方法最后多一个 BlockException,其余与原方法test一致.
public String handleException((String s1,String s2,BlockException ex){
    return "被流控了";
}

// Fallback 异常处理函数,参数与原方法test一致或加一个 Throwable 类型的参数.
public String fallbackException((String s1,String s2,Throwable t){
    return "被异常降级了";
}


@PostConstruct
private static void initFlowRules(){
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("helloWorld");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // Set limit QPS to 20.
    rule.setCount(2);
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

使用注意事项:

  • 方法必须和被保护资源处于同一个类(可以把blockHandler和fallback方法提成公共方法,但要增加额外参数,下面说)
  • 方法参数列表和受保护资源一致(blockHandler最后增加一个BlockException,fallback可选增加Throwable)
  • 方法返回值必须和受保护资源相同

因为一个规则可以用于多个资源,那么对应的blockHandler和fallback方法也可以用于多个地方,可以把这个方法提到一个公共类中

  • 把blockHandler和fallback方法提到一个工具类中
public class ExceptionUtil {

    public static String fallbackException(Throwable t){
        return "===被异常降级啦===";
    }

    public static String handleException(BlockException ex){
        return "===被限流啦===";
    }
}

  • Controller中,对应的注解要增加参数声明用的是哪个类的blockHandler和fallback
@SentinelResource(value = RESOURCE_NAME,
        blockHandler = "handleException",blockHandlerClass = ExceptionUtil.class,
        fallback = "fallbackException",fallbackClass = ExceptionUtil.class)

Sentinel控制台配置流控规则

虽然有了注解式开发,但是每次还要去写initFlowRules中的代码去创建流控规则,那么可不可以通过不写代码,而是页面的方式去配置流控规则呢?答案是可以的

Sentinel控制台

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。

  • 下载Sentinel控制台jar包

下载地址:https://github.com/alibaba/Sentinel/releases/download/1.8.6/sentinel-dashboard-1.8.6.jar
注意:Sentinel 控制台目前仅支持单机部署。

  • 启动sentinel控制台

java -Dserver.prot:8080 -jar sentinel-dashboard-1.8.6.jar

如若8080端口冲突,可使用 -Dserver.port=新端口 进行设置

  • 访问http://localhost:8080/#/login ,默认用户名密码: sentinel/sentinel
    在这里插入图片描述
    现在控制台是没有注册到服务的,所以页面没有任何东西,但有的博客启动是可以看到一个微服务的,那是因为启动命令时,加了参数,Sentinel控制台把自己作为一个微服务注册到自己上了,这里感觉没必要,我就简化了启动命令,所以这里看到的是空的

java应用到 Sentinel控制台

  • pom依赖
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.6</version>
</dependency>
  • Java应用启动时添加相应的 JVM 参数

    • 应用名称:-Dproject.name=xxxx
    • 控制台地址:-Dcsp.sentinel.dashboard.server=ip:port
    • 应用启动 HTTP API Server 的端口号:-Dcsp.sentinel.api.port=xxxx (默认是 8719),端口冲突会依次递增,可以不指定
  • sentinel日志查看路径(免得启动时报错不知道怎么排查):

    • 控制台推送规则的日志默认位于 ${user.home}/logs/csp/sentinel-dashboard.log
    • 客户端接收规则日志默认位于 ${user.home}/logs/csp/sentinel-record.log.xxx
      在这里插入图片描述

- 查看监控台 java项目启动后,去查看监控台,`发现此时还是没有服务,是因为没有请求的原因`;请求了服务后,就可以发现服务了 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4614ba4ebdf5468c9af27a2bf9b477f6.gif#pic_center) `注意: Sentinel 会在客户端首次调用时候进行初始化,开始向控制台发送心跳包。因此需要确保客户端有访问量,才能在控制台上看到监控数据。`
  • 在控制台创建流控规则
    在这里插入图片描述

  • 代码中注释掉初始化的流控规则(如果不注释,同规则名下,以代码中为主)

 /*@PostConstruct
 private static void initFlowRules(){
     List<FlowRule> rules = new ArrayList<>();
     FlowRule rule = new FlowRule();
     rule.setResource(RESOURCE_NAME);
     rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
     // Set limit QPS to 20.
     rule.setCount(2);
     rules.add(rule);
     FlowRuleManager.loadRules(rules);
 }*/
  • 还是以切面去配置资源
//注意:这里RESOURCE_NAME要和控制台配置的流控规则名称一样,否则没用
//private final static String RESOURCE_NAME = "HelloWorld";
@SentinelResource(value = RESOURCE_NAME,blockHandler = "blockHandlerTest",
        fallback = "fallbackTest")
@GetMapping(value = "/test2")
@ResponseBody
public String test2(@RequestParam(defaultValue = "false") Boolean flag){
    System.out.println("hello world");
    if (flag){
       throw new RuntimeException("手动报错");
    }
    return "正常返回";
}

public String blockHandlerTest(Boolean flag,BlockException e){
    System.out.println("被流控了");
    return "被流控了";
}

public String fallbackTest(Boolean flag,Throwable e){
    System.out.println("被降级了");
    return "被降级了";
}

这样是不是只要代码中我可以直接以切面形式去使用规则,然后在代码中随便配置/修改规则,还可以配置熔断规则、热点规则等

Java应用和Sentinel Dashboard通信原理

在这里插入图片描述

微服务整合Sentinel

  • pom依赖(只引这一个依赖,上面所有依赖不用再引入)
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2021.0.1.0</version>
</dependency>
  • yml文件配置参数
spring:
  application:
    name: mall-user-2 #微服务名称
    
  cloud:
    sentinel:
      transport:
        # 添加sentinel的控制台地址
        dashboard: 127.0.0.1:8080
        # 指定应用与Sentinel控制台交互的端口,应用会起一个HttpServer占用该端口
        # port: 8719

注意:当 SpringBoot 应用接入 Sentinel starter 后,可以针对某个 URL 进行流控。所有的URL就自动成为 Sentinel 中的埋点资源,可以针对某个 URL 进行流控。或者使用@SentinelResource 注解用来标识资源是否被限流、降级。可以看下MVC的拦截器

public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        try {
            String resourceName = getResourceName(request);

            if (StringUtil.isEmpty(resourceName)) {
                return true;
            }
            
            if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
                return true;
            }
            
            // Parse the request origin using registered origin parser.
            String origin = parseOrigin(request);
            String contextName = getContextName(request);
            ContextUtil.enter(contextName, origin);
            // mvc的拦截器,已经帮我们去流控了
            Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
            request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
            return true;
        } catch (BlockException e) {
            try {
                handleBlockException(request, response, e);
            } finally {
                ContextUtil.exit();
            }
            return false;
        }
    }
}
  • java代码(默认以url为被保护资源)

// 微服务整合Sentinel,资源名默认为url路径,需要在Sentinel控制台配置好
@GetMapping(value = "/test3")
@ResponseBody
public String test3(){
    // 被保护的逻辑
    System.out.println("hello world 3");
    return "正常返回";
}
  • 启动sentinel控制台,在sentinel控制台中设置流控规则
    • 资源名: 接口的API

先访问一下/test3这个url,然后去控制台看
在这里插入图片描述
簇点链路中找到URL后,去配置流控规则(资源名默认就是URL,否则MVC拦截器中的流控没用)
在这里插入图片描述

在这里插入图片描述

如果访问频率超过QPS/2,那么就会在页面报错Blocked by Sentinel (flow limiting),但这报错看着很不舒服,所以可以自定义配置报错信息

  • 定义类去实现BlockExceptionHandler
@Component
@Slf4j
public class MySentinelBackMsg implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
    	// 定义个ExceptionMsg实体类
        ExceptionMsg res = null;
        log.info("--------自定义的Sentinel限流和熔断规则返回值-------");
        if (e instanceof FlowException){
            res.setCode(101);
            res.setMsg("被限流了");
        }
        if (e instanceof DegradeException){
            res.setCode(102);
            res.setMsg("被降级了");
        }
    }
}

自定义完报错信息,重新启动后发现,Sentinel控制台没有URL的流控规则了,还需要重新去配置,是因为流控规则是存在内存中,持久化是肯定要解决的问题,其实可以通过nacos配置中心或数据库进行持久化,然后读取规则

RestTemplate整合Sentinel(了解)

Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造 RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。

  • RestTemplate添加@SentinelRestTemplate注解
@Bean
@LoadBalanced
@SentinelRestTemplate(
        blockHandler = "handleBlockException",blockHandlerClass = ExceptionUtil.class,
        fallback = "handleFallback",fallbackClass = ExceptionUtil.class
)
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@SentinelRestTemplate 注解的属性支持限流(blockHandler, blockHandlerClass)和降级(fallback, fallbackClass BlockException中的DegradeException)的处理。

  • 添加yml配置,开启sentinel对resttemplate的支持,默认开启,可忽略
#开启sentinel对resttemplate的支持,false则关闭 ,默认true
resttemplate: 
  sentinel: 
    enabled: true
  • 在sentinel控制台中对被保护的restTemplate资源进行限流配置
    和之前配置流控规则一样,不过有两个维度(微服务维度和接口维度)

OpenFeign整合Sentinel

  1. yml配置文件中开启 Sentinel 对 Feign 的支持
feign:
  sentinel:
    enabled: true  #开启SentinelFeign 的支持
  1. 在Feign的声明式接口上添加fallback或者fallbackFactory属性
  • fallback注解:限流、熔断、热点规则都在fallback的类中返回
@FeignClient(value = "mall-order",path = "/order",fallback = FallbackOrderFeignService.class)
public interface OrderFeignService {
    @RequestMapping("/findOrderByUserId/{userId}")
    public R findOrderByUserId(@PathVariable("userId") Integer userId);
}

@Component   //必须交给spring 管理
public class FallbackOrderFeignService implements OrderFeignService {
    @Override
    public R findOrderByUserId(Integer userId) {
        return R.error(-1,"=======服务降级了========");
    }
}
  • fallbackFactory注解:实现FallbackFactory<自己Service的泛型>接口,比fallback的优势是方法可以多传个Throwable 参数,可以去具体区分是限流、熔断、热点报错,然后返回不同的值
@FeignClient(value = "mall-order",path = "/order",fallbackFactory = FallbackOrderFeignServiceFactory.class)
public interface OrderFeignService {

    @RequestMapping("/findOrderByUserId/{userId}")
    public R findOrderByUserId(@PathVariable("userId") Integer userId);
}

@Component
public class FallbackOrderFeignServiceFactory implements FallbackFactory<OrderFeignService> {
    @Override
    public OrderFeignService create(Throwable throwable) {
		//这里可以具体区分Throwable的类型,然后返回不同的值
        return new OrderFeignService() {
            @Override
            public R findOrderByUserId(Integer userId) {
                return R.error(-1,"=======服务降级了========");
            }
        };
    }
}
  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值