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
- yml配置文件中开启 Sentinel 对 Feign 的支持
feign:
sentinel:
enabled: true #开启Sentinel 对 Feign 的支持
- 在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,"=======服务降级了========");
}
};
}
}
原文地址:https://blog.csdn.net/L_Mr_ll/article/details/140451965
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!