您的位置:首页 > 科技 > IT业 > Sentinel使用

Sentinel使用

2024/10/6 6:01:17 来源:https://blog.csdn.net/L_Mr_ll/article/details/140451965  浏览:    关键词:Sentinel使用

文章目录

    • Sentinel 介绍
      • Sentinel 基本概念
        • 服务雪崩
          • 解决方案
            • 超时机制
            • 服务限流
            • 舱壁隔离(资源隔离)
            • 服务熔断降级
    • Sentinel or Hystrix对比
    • Sentinel基本使用
      • 书写代码控制保护的资源
      • 切面控制保护的资源
    • Sentinel控制台配置流控规则
      • Sentinel控制台
      • java应用到 Sentinel控制台
      • Java应用和Sentinel Dashboard通信原理
    • 微服务整合Sentinel
      • RestTemplate整合Sentinel(了解)
      • OpenFeign整合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")@ResponseBodypublic 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 {@Beanpublic 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 会在客户端首次调用时候进行初始化,开始向控制台发送心跳包。因此需要确保客户端有访问量,才能在控制台上看到监控数据。`
  • 在控制台创建流控规则
    在这里插入图片描述

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

 /*@PostConstructprivate 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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> {@Overridepublic OrderFeignService create(Throwable throwable) {//这里可以具体区分Throwable的类型,然后返回不同的值return new OrderFeignService() {@Overridepublic R findOrderByUserId(Integer userId) {return R.error(-1,"=======服务降级了========");}};}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com