资源是Sentinel的关键概念。它可以是Java应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。
只要通过Sentinel API定义的代码,就是资源,能够被Sentinel保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
Sentinel可以简单的分为Sentinel核心库和Dashboard。核心库不依赖Dashboard,但是结合Dashboard可以取得最好的效果。
我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。使用
Sentinel来进行资源保护,主要分为几个步骤:
1.定义资源
2.定义规则
3.检验规则是否生效
先把可能需要保护的资源定义好(埋点),之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。
对于主流的框架,我们提供适配,只需要按照适配中的说明配置,Sentinel就会默认定义提供的服务,方法等为资源。
主流框架的默认适配为了减少开发的复杂程度,我们对大部分的主流框架,例如Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor等都做了适配。您只需要引入对应的依赖即可方便地整合Sentinel。
可以参见:主流框架的适配。
抛出异常的方式定义资源SphU包含了try-catch风格的API。用这种方式,当资源发生了限流之后会抛出BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。
示例代码如下:
//1.5.0版本开始可以利用try-with-resources特性(使用有限制)
//资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
try(Entry entry = SphU.entry("resourceName"))
{
//被保护的业务逻辑
//do something here...
}
catch (BlockException ex)
{
//资源访问阻止,被限流或被降级
//在此处进行相应的处理操作
}
特别地,若entry的时候传入了热点参数,那么exit的时候也一定要带上对应的参数(exit(count,args)),否则可能会有统计错误。这个时候不能使用try-with-resources的方式。
另外通过Tracer.trace(ex)来统计异常信息时,由于try-with-resources语法中catch调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在try-with-resources的catch块中调用Tracer.trace(ex)。
手动exit示例:
Entry entry = null;
//务必保证finally会被执行
try{
//资源名可使用任意有业务语义的字符串,注意数目不能太多(超过1K),超出几千请作为参数传入而不要直接作为资源名
//EntryType代表流量类型(inbound/outbound),其中系统规则只对IN类型的埋点生效
entry = SphU.entry("自定义资源名");
//被保护的业务逻辑
//do something...
}
catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
}
catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
}
finally {
// 务必保证 exit,务必保证每个entry与exit配对
if(entry!=null){
entry.exit();
}
}
热点参数埋点示例:
Entry entry = null;
try {
// 若需要配置例外项,则传入的参数只支持基本类型。
// EntryType代表流量类型,其中系统规则只对IN类型的埋点生效
// count大多数情况都填1,代表统计为一次调用。
entry =SphU.entry(resourceName,EntryType.IN,1,paramA,paramB);
// Your logic here.
}
catch(BlockException ex) {
//Handle request rejection.
}
finally {
// 注意:exit的时候也一定要带上对应的参数,否则可能会有统计错误。
if(entry!=null){
entry.exit(1,paramA,paramB);
}
}
SphU.entry()的参数描述:
参数名 | 类型 | 解释 | 默认值 |
---|---|---|---|
entryType | EntryType | 资源调用的流量类型,是入口流量(EntryType.IN)还是出口流量(EntryType.OUT),注意系统规则只对IN生效 | EntryType.OUT |
count | int | 本次资源调用请求的token数目 | 1 |
args | Object[] | 传入的参数,用于热点参数限流 | 无 |
注意:
SphU.entry(xxx)需要与entry.exit()方法成对出现,匹配调用,否则会导致调用链记录异常,抛出ErrorEntryFreeException异常。常见的错误:
SphO提供if-else风格的API。用这种方式,当资源发生了限流之后会返回false,这个时候可以根据返回值,进行限流之后的逻辑处理。
示例代码如下:
// 资源名可使用任意有业务语义的字符串
if(SphO.entry("自定义资源名")){
//务必保证finally会被执行
try{
// 被保护的业务逻辑
}
finally{
SphO.exit();
}
}
else{
//资源访问阻止,被限流或被降级
//进行相应的处理操作
}
注意:
SphO.entry(xxx)需要与SphO.exit()方法成对出现,匹配调用,位置正确,否则会导致调用链记录异常,抛出ErrorEntryFreeException异常。
实例代码:
@GetMapping(value = "/helloWorld2")
public String helloWorld2() {
// 资源名可使用任意有业务语义的字符串
if(SphO.entry("helloWorld2")){
//务必保证finally会被执行
try{
// 被保护的业务逻辑
long millis = System.currentTimeMillis();
log.info("helloWorld2="+millis);
return "helloWorld2="+millis;
}
finally{
SphO.exit();
}
}
else{
//资源访问阻止,被限流或被降级
//进行相应的处理操作
log.error("系统繁忙,请稍后再试!");
return "系统繁忙,请稍后再试!";
}
}
sentinel控制台添加流控规则:
访问http://localhost/helloWorld2
正常访问显示:
如果频繁访问会出现:
触碰降级规则,返回降级信息;