Sentinel快速入门:这可能是目前最好的分布式系统限流降级框架

简介: 在分布式系统中,许多服务之间通过远程调用实现信息交互,调用时难免会出现调用失败的情况,Sentinel能保证在一个服务出现问题的情况下,不会导致整体服务失败,防止服务雪崩,提高分布式系统的可用性。

听说微信搜索《Java鱼仔》会变更强!


本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦


(一)概述


在分布式系统中,许多服务之间通过远程调用实现信息交互,调用时难免会出现调用失败的情况,Sentinel能保证在一个服务出现问题的情况下,不会导致整体服务失败,防止服务雪崩,提高分布式系统的可用性。


常用的容错方式有:


1、超时:设置比较短的超时时间,如果调用不成功,在很短时间内就释放连接,避免大量线程堵塞等待。


2、限流:超过设置的阈值就拒绝请求。


3、断路器:保护服务过载,当有服务发生无法调用请求堆积时,能够及时切换该服务,防止整个服务的崩溃。


Sentinel的地位和SpringCloud中的Hystrix类似,Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。


(二)SpringCloudAlibaba集成Sentinel


首先我们在sentinel的github官网上下载sentinel-dashboard-1.7.2.jar


然后通过jar命令启动起来,端口默认8080,直接ip+端口访问,这是sentinel的控制后台,账号密码都是sentinel


网络异常,图片无法展示
|


接着在项目中引入依赖


<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
复制代码

在配置文件中,输入Sentinel的后台地址,地址和端口写自己的,端口默认8080


spring.cloud.sentinel.transport.dashboard=localhost:8080
复制代码

启动后在Sentinel后台就可以看到这个应用


网络异常,图片无法展示
|


sentinel控制台调用的API默认是http://ip:8719/api,在本地启动了引入sentinel依赖的项目启动后,默认在**8719**这个端口上会暴露一个api出来。


(三)sentinel的流控规则


写了一个简单的请求:


@RestControllerpublicclassTestController {
@GetMapping("/test")
publicStringtest(){
return"test";
    }
}

接下来通过这个请求去了解sentinel的流控规则,所谓流控规则,就是对请求的控制


网络异常,图片无法展示
|


资源名:默认是请求的路径


针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)


阈值类型:可选QPS和线程数,可以设置达到多少后执行流控策略


是否集群:你的环境是否是集群


流控模式


1、直接:直接对该资源进行控制,比如上面的/test访问达到阈值,就限流


2、关联:当关联的资源达到阈值时,就限流自己。


网络异常,图片无法展示
|


看图,如果/test2访问达到阈值,就限流/test


3、链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以限流),这个链路的名称可以从簇点链路中获取。


网络异常,图片无法展示
|


流控效果


1、快速失败:直接失败


2、Warm Up:预热模式,根据codeFactory的值(默认3),从阈值/codeFactory,经过预热时长,才达到设置的QPS阈值。比如设置QPS为90,设置预热为10秒,则最初的阈值为90/3=30,经过10秒后才达到90。


3、排队等待:比如设置阈值为10,超时时间为500毫秒,当第11个请求到的时候,不会直接报错,而是等待500毫秒,如果之后阈值还是超过10,则才会被限流。


设置完流控规则后,不停刷新去触发到阈值,看到出现了下面的提示:


网络异常,图片无法展示
|


这样的提示不太友好,可以自定义限流后返回的数据信息,分别对应于五种限流异常:


publicclassMyBlockExceptionimplementsBlockExceptionHandler {
@Overridepublicvoidhandle(HttpServletRequesthttpServletRequest, HttpServletResponsehttpServletResponse, BlockExceptione) throwsException {
JSONObjectobject=newJSONObject();
if (einstanceofFlowException){
object.put("status","100");
object.put("message","接口限流");
object.put("data",null);
        }elseif (einstanceofDegradeException){
object.put("status","101");
object.put("message","服务降级");
object.put("data",null);
        }elseif (einstanceofParamFlowException){
object.put("status","102");
object.put("message","热点参数限流");
object.put("data",null);
        }elseif (einstanceofSystemBlockException){
object.put("status","103");
object.put("message","触发系统保护");
object.put("data",null);
        }elseif (einstanceofAuthorityException){
object.put("status","104");
object.put("message","授权规则不通过");
object.put("data",null);
        }
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
newObjectMapper().writeValue(httpServletResponse.getWriter(),object);
    }
}

代码中定义了五种不同的


(四)sentinel的降级规则


降级规则指的是在应用高峰期,将个别的服务关闭,使得能够有更多地资源去处理重要的业务,比如双十一的时候,我们会发现支付宝的部分功能会被暂时关闭。


网络异常,图片无法展示
|


RT模式:平均响应时间、当1S内持续进入N个请求,如果平均响应时间超过阈值,那么在接下来的时间窗口内,按降级逻辑进行处理(报一个DegradeException错误)。


异常比例:可以输入一个0.0~1.0的数字,表示出现异常的比例。如果一秒内超过这个比例,那么在接下来的时间窗口内,按降级逻辑进行处理。


异常数:指当资源近一分钟的异常数超过阈值之后会,那么在接下来的时间窗口内,按降级逻辑进行处理。


(五)sentinel的热点规则


所谓热点规则,就是对某些经常访问的数据(热点数据),对其访问进行限制,

比如以商品ID为参数,限制这个商品的访问次数。在代码中需要对要限制的请求进行埋点


@GetMapping("/test3")
@SentinelResource(value="test3",blockHandler="handHotKey")
publicStringtest3(@RequestParam(value="a",required=false)Stringa,
@RequestParam(value="b",required=false)Stringb){
return"test3"+a+b;
}
publicStringhandHotKey(Stringa1, Stringa2, BlockExceptione){
return"热点数据限流";
}

设置热点规则,这里的资源名就是@SentinelResource中所设置的value,参数索引的表示对第几个参数进行限流控制,阈值和窗口时长表示在1秒内如果有2个对参数0的请求,就限流。限流后会执行自己设置的blockHandler方法。


网络异常,图片无法展示
|


高级设置中可以设置参数例外项,即根据设置参数的值进行限流:


网络异常,图片无法展示
|


这样设置后,如果访问/test3?a=1,则按照下面的阈值进行控制。


(六)Sentinel的系统规则


通过监控系统的一些参数进行规则限流:


网络异常,图片无法展示
|


Load:这个参数只能在Linux或类Unix机器生效,将系统的1分钟的loadAverage作为指标,这个值一般设置为CPU核心数量*2.5。


RT:当单台机器上所有入口流量的平均RT达到阈值就触发系统保护,单位是毫秒。


并发线程数:当单台机器上所有入口流量的并发线程数达到阈值就触发系统保护。


入口QPS:当单台机器上所有入口流量的QPS达到阈值就触发阈值就触发系统保护。


CPU使用率:当系统CPU使用率超过阈值就触发系统保护。


(七)Sentinel的授权规则


授权规则可以指定哪些请求可以访问哪些不能访问,首先来看如何配置:


网络异常,图片无法展示
|


资源名就是请求名,流控应用中可以手动输入一些应用名,如果是白名单,则流控应用中设置的这些可以访问,如果是黑名单则流控应用中设置的不能访问。

接着需要在代码中去获取请求:


@ComponentpublicclassMyRequestOriginParserimplementsRequestOriginParser {
@OverridepublicStringparseOrigin(HttpServletRequesthttpServletRequest) {
Stringorigin=httpServletRequest.getParameter("origin");
if (origin==null){
thrownewIllegalArgumentException("origin参数未指定");
        }
returnorigin;
    }
}

通过设置后,所有请求必须带上参数origin=XXX,以上面的配置为例,只有origin=javayz的请求才能通过访问。如果不喜欢参数的方式,可以在代码中换成header传递,效果一样。


(八)Sentinel控制台与服务之间的通信原理


使用Sentinel需要引入引入sentinel依赖,其中sentinel-transport-simple-http依赖会将微服务注册到SentinelDashboard中。启动微服务之后,会在8719端口自动开放一系列api接口,我们也可以通过http://ip:8719/api访问到这些api接口,SentinelDashboard就是通过这些API与微服务之间进行通信。

这个8719端口可以在配置文件中修改:


spring.cloud.sentinel.transport.port=8719

(九)Sentinel的保护规则


默认情况下,我们代码中的所有GetMapping、PostMapping请求都会被Sentinel保护,也就是都会经过Sentinel的拦截器,但是也可以手动关闭这个拦截。

spring.cloud.sentinel.filter.enabled=false

这样的话sentinel就没法捕捉请求了。但是还是可以通过代码的方式使用Sentinel

@GetMapping("/test4")
publicStringtest4(){
ContextUtil.enter("test4","abc");
Entryentry=null;
try {
entry=SphU.entry("test4");
//业务代码return"业务处理结束";
    } catch (BlockExceptione) {
e.printStackTrace();
//一系列的异常处理。参考MyBlockException//.....return"触发限流";
    }catch (NullPointerExceptione){
//对异常进行监控Tracer.trace(e);
return"空指针异常";            
    }finally {
if (entry!=null){
entry.exit();
        }
ContextUtil.exit();
    }
}

或者还可以使用注解方式加入埋点

@SentinelResource(value="test3",blockHandler="handHotKey")

这样配置后就增加了资源test3的拦截,如果触发限流策略,就会进入当前类的handHotKey方法,或者配置blockHandlerClass,就会进入blockHandlerClass所配置类中的handHotKey方法。


(十)RestTemplate整合Sentinel


首先需要在配置文件中开启对RestTemplate的支持,默认也是true


resttemplate.sentinel.enabled=true

接着在RestTemplate注入Bean的代码中增加一个Sentinel注解:

@SentinelRestTemplate(blockHandler="fallback",blockHandlerClass=MyBlockHandlerClass.class)
@LoadBalanced//负载均衡@BeanpublicRestTemplaterestTemplate(){
returnnewRestTemplate();
}

调用的限流处理方法如下:

publicclassMyBlockHandlerClass {
publicstaticSentinelClientHttpResponseblock(){
returnnewSentinelClientHttpResponse("block info");
    }
}

(十一)Feign整合Sentinel


首先需要在配置文件中开启对Feign的支持,默认为false:

feign.sentinel.enabled=true

接在在@FeignClient注解中增加fallback的类:

@FeignClient(name="nacos-discovery-provider",fallback=TestServiceFallback.class,configuration=FeignConfiguration.class)
publicinterfaceTestService {
@GetMapping("/{name}")
Stringindex(@PathVariable("name") Stringstring);
}
最后设置规则后就会触发fallback中对应的方法,具体实现publicclassTestServiceFallbackimplementsTestService{
@OverridepublicStringindex(Stringstring) {
return"fallback";
    }
}

(十二)Sentinel的持久化


默认模式:


在前面介绍Sentinel时,会发现每次重启微服务后Sentinel中的配置都会丢失,这是因为API将规则推送到了客户端的内存中,重启后就消失了。


Pull模式


在Sentinel Dashboard中设置规则之后,推送给客户端后不仅保存在内存中,还会保存到本地文件中。Pull模式需要通过代码实现,这段代码可以直接拿去复用:


@Slf4jpublicclassFileDataSourceInitimplementsInitFunc {
@Overridepublicvoidinit() throwsException {
StringruleDir=System.getProperty("user.home")+"/sentinel/rules";
log.info(ruleDir);
//限流规则路径StringflowRulePath=ruleDir+"/flow-rule.json";
//降级规则路径StringdegradeRulePath=ruleDir+"/degrade-rule.json";
//热点规则路径StringparamFlowRulePath=ruleDir+"/param-flow-rule.json";
//系统规则路径StringsystemRulePath=ruleDir+"/system-rule.json";
//权限规则路径StringauthorityRulePath=ruleDir+"/authority-rule.json";
this.mkdirIfNotExists(ruleDir);
this.createFileIfNotExists(flowRulePath);
this.createFileIfNotExists(flowRulePath);
this.createFileIfNotExists(flowRulePath);
this.createFileIfNotExists(flowRulePath);
this.createFileIfNotExists(flowRulePath);
// 流控规则,可读取数据ReadableDataSource<String, List<FlowRule>>flowRuleRDS=newFileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser        );
//将可读数据源注入到FlowRuleManager,当文件发生变化时就会更新规则到缓存FlowRuleManager.register2Property(flowRuleRDS.getProperty());
//流控规则:可写数据源WritableDataSource<List<FlowRule>>flowRuleWDS=newFileWritableDataSource<List<FlowRule>>(
flowRulePath,
this::encodeJson        );
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
//降级规则:可读数据源ReadableDataSource<String, List<DegradeRule>>degradeRuleRDS=newFileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser        );
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
//降级规则:可写数据源WritableDataSource<List<DegradeRule>>degradeRuleWDS=newFileWritableDataSource<List<DegradeRule>>(
flowRulePath,
this::encodeJson        );
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
//热点参数:可读数据源ReadableDataSource<String, List<ParamFlowRule>>paramFlowRuleRDS=newFileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser        );
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
//热点参数:可写数据源WritableDataSource<List<ParamFlowRule>>paramFlowRuleWDS=newFileWritableDataSource<List<ParamFlowRule>>(
paramFlowRulePath,
this::encodeJson        );
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
//系统规则:可读数据源ReadableDataSource<String, List<SystemRule>>systemRuleRDS=newFileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser        );
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
//系统规则:可写数据源WritableDataSource<List<SystemRule>>systemRuleWDS=newFileWritableDataSource<List<SystemRule>>(
systemRulePath,
this::encodeJson        );
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
//授权规则:可读数据源ReadableDataSource<String, List<AuthorityRule>>authorityRuleRDS=newFileRefreshableDataSource<>(
authorityRulePath,
authorityRuleListParser        );
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>>authorityRuleWDS=newFileWritableDataSource<List<AuthorityRule>>(
authorityRulePath,
this::encodeJson        );
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    }
privateConverter<String,List<FlowRule>>flowRuleListParser=source->JSON.parseObject(
source,
newTypeReference<List<FlowRule>>(){}
    );
privateConverter<String,List<DegradeRule>>degradeRuleListParser=source->JSON.parseObject(
source,
newTypeReference<List<DegradeRule>>(){}
    );
privateConverter<String,List<SystemRule>>systemRuleListParser=source->JSON.parseObject(
source,
newTypeReference<List<SystemRule>>(){}
    );
privateConverter<String,List<AuthorityRule>>authorityRuleListParser=source->JSON.parseObject(
source,
newTypeReference<List<AuthorityRule>>(){}
    );
privateConverter<String,List<ParamFlowRule>>paramFlowRuleListParser=source->JSON.parseObject(
source,
newTypeReference<List<ParamFlowRule>>(){}
    );
privatevoidmkdirIfNotExists(StringfilePath) {
Filefile=newFile(filePath);
if (!file.exists()){
file.mkdirs();
        }
    }
privatevoidcreateFileIfNotExists(StringfilePath) throwsIOException {
Filefile=newFile(filePath);
if (!file.exists()){
file.createNewFile();
        }
    }
private<T>StringencodeJson(Tt){
returnJSON.toJSONString(t);
    }
}


sentinel的数据持久化是通过SPI机制实现的,因此需要在resource下新建文件夹META-INF/services,然后新建一个文件com.alibaba.csp.sentinel.init.InitFunc

文件中写入上面这个类的全限定名:


网络异常,图片无法展示
|


之后产生规则后就会在代码中设定的路径下产生json文件,重启后之前的配置也不会消失。


Push模式:客户端通过注册监听器的方式时刻监听变化,比如使用Nacos、Zookeeper等配置中心,这种方式保证了很好的实时性和一致性,生产环境中一般采用push模式。我们用Nacos实现


1、添加sentinel-datasource-nacos依赖


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

2、配置持久化数据源

spring.cloud.sentinel.datasource.ds1.nacos.server-addr=192.168.78.128:8848spring.cloud.sentinel.datasource.ds1.nacos.data-id=${spring.application.name}.jsonspring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUPspring.cloud.sentinel.datasource.ds1.nacos.data-type=jsonspring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow


3、在Nacos中手动添加配置文件,这里的配置文件取的就是和本地配置文件相同格式


网络异常,图片无法展示
|


[
    {
"clusterMode":false,
"controlBehavior":0,
"count":2,
"grade":1,
"limitApp":"default",
"maxQueueingTimeMs":500,
"resource":"/test",
"strategy":0,
"warmUpPeriodSec":10    }
]

push模式目前还有缺点,Nacos修改配置文件后可同步到Sentinel,但是在Sentinel中修改配置后无法同步到Nacos,需要手动去同步数据。


总结


工作中不一定会用到这些框架,但是我们需要有对应的知识储备。我是鱼仔,我们下期再见!

相关文章
|
22天前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
22天前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
21天前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
34 6
|
21天前
|
数据库
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
28 6
|
19天前
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
23 1
|
1月前
|
机器学习/深度学习 自然语言处理 并行计算
DeepSpeed分布式训练框架深度学习指南
【11月更文挑战第6天】随着深度学习模型规模的日益增大,训练这些模型所需的计算资源和时间成本也随之增加。传统的单机训练方式已难以应对大规模模型的训练需求。
116 3
|
1月前
|
机器学习/深度学习 并行计算 Java
谈谈分布式训练框架DeepSpeed与Megatron
【11月更文挑战第3天】随着深度学习技术的不断发展,大规模模型的训练需求日益增长。为了应对这种需求,分布式训练框架应运而生,其中DeepSpeed和Megatron是两个备受瞩目的框架。本文将深入探讨这两个框架的背景、业务场景、优缺点、主要功能及底层实现逻辑,并提供一个基于Java语言的简单demo例子,帮助读者更好地理解这些技术。
76 2
|
2月前
|
分布式计算 Hadoop
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
51 1
|
3月前
|
数据采集 分布式计算 MaxCompute
MaxCompute 分布式计算框架 MaxFrame 服务正式商业化公告
MaxCompute 分布式计算框架 MaxFrame 服务于北京时间2024年09月27日正式商业化!
100 3
|
2月前
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
51 0