Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
同hystrix相比,除了服务熔断,还可以进行服务降级、服务流控等。
sentinel有多种使用方式,常见的有单独使用dashboard、同Gateway结合使用、同Spring Cloud/Alibaba结合使用、同RPC组件结合使用等。建议结合项目选择。开源项目Ruoyi-Cloud选择结合Gateway使用。
在服务端用maven引入jar包;
控制面板dashboard可以从https://github.com/alibaba/Sentinel/releases
下载sentinel-dashboard-$version.jar
包。
Sentinel基本概念
- 资源 resource:Sentinel中一切抽象成资源,服务、服务中的方法,甚至是一段代码,都可以被定义成资源,然后被Sentinel管理;
- 规则 rules : 流量规则,熔断规则,负载规则,,,
五大规则
流控规则
降级规则
监控应用中资源调用请求,达到阈值时自动触发熔断降级。
热点规则
系统规则
系统自适应限流
授权规则
黑白名单控制
快速开始
本文以Spring Cloud Alibaba中引入Sentinel为例,其他方式引入请参考官方wiki。
在服务中引入Sentinel,然后在dashboard中配置规则。
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
在配置文件中开启Sentinel
spring.cloud.sentinel.enabled=true
# 指定sentinel dashboard web地址
spring.cloud.sentinel.transport.dashboard= #dashboard的ip:端口
# 指定snetinel组件和sentinel dashboard组件通信地址(用tcp更高效的通信,端口默认为8719)
spring.cloud.sentinel.transport.port=8719
DashBoard控制台方式
使用java -jar
命令启动dashboard控制台:
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\sentinel\sentinel-dashboard-1.8.0.jar
Sentinel提供了一个可视化的操作平台,安装好之后,在浏览器中输入(http://localhost:8718 (opens new window))就可以访问了,默认的用户名和密码都是sentinel
dashboard在有一次调用发生后,才能在web界面看到服务(这算懒加载么?)
调用后sentinel才会产生资源调用日志,有日志生成后才会dashboard出现服务,多调用几次才会出现qps等信息。
启动参数说明:
-Dserver.port:指定启动的端口,默认8080
-Dproject.name:指定本服务的名称
-Dcsp.sentinel.dashboard.server:指定sentinel控制台的地址,用于将自己注册进入实现监控自己
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 “sentinel”,默认值为 “sentinel”
-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 “123456”,默认值为 “sentinel”
-Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟,需要注意的是,部署多台控制台时,session 默认不会在各实例之间共享,这一块需要自行改造。
熔断 降级规则
用来避免服务雪崩。触发熔断后对该服务的调用不可用。
在dashboard中选择“降级规则”
Sentinel熔断策略
触发熔断(断路器打开)的三种策略
RT 根据请求响应时间熔断
异常比例
抛出的异常的比例。如设置为0.1,则比例超过10%时触发熔断
异常数
时间窗口(单位:秒)结束后,断路器关闭。
热点规则
也成为热点参数限流,对携带了指定参数的资源进行限流。
如https://editor.csdn.net/md/?articleId=131038775
,参数为articleId
只能使用QPS模式。
不能使用资源路径的方式配置,只能使用资源别名。
资源别名:在处理请求的方法上使用@SentinelResource(value="别名")
注解指定别名。mvc模式的话就放在controller层方法上。
在[统计窗口时长]内,对带有第[参数索引]个参数的请求,超过阈值时进行限流。
参数索引从0开始计数,代表请求中的第几个参数。
@SentinelResource定义资源
@SentinelResource(value=“别名”, fallback=“”, blcokHandler=“限流处理method名”)
fallback处理业务异常,blockHandler处理限流。两参数的值都是指定方法名。
限流处理method例子:
注:该例子中controller层方法带Integer id参数
@Controller
@SentinelResource(value="别名", fallback="", blcokHandler="限流处理method名")
public String controllerMethod(Integer id){
//Method body...
}
private String blockHandler(Integer id, BlockException e){
if(e instanceof FlowException){
return "当前请求过于火爆,已被降级";
}
if(e instanceof DegradeException){
return "当前请求过于火爆,已被降级";
}
if(e instanceof ParaFlowException){
return "当前请求过于火爆,已被参数热点限流";
}
return "服务器过于火爆,请稍后再试捏~(づ ̄3 ̄)づ╭❤~kira";
}
@SentinelResource 注解不支持 private 方法。默认情况下,Sentinel 对控制资源的保护处理是直接抛出异常,这样对用户不友好,所以我们需要通过可选的异常处理 blockHandler 和 fallback 配置项处理一下异常信息。
-
blockHandler/blockHandlerClass: blockHandler 指定函数负责处理 BlockException 异常,可选项。blockHandler 函数默认需要和原方法在同一个类中,通过指定 blockHandlerClass 为对应类的 Class 对象,则可以指定其他类中的函数,但注意对应的函数必需为 static 函数,否则无法解析。
blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型必须和原方法一致并且最后加一个类型为 BlockException 的异常参数。
可以通过这种方式拿到请求发来的参数。 @SentinelResource和blockHandler都要求public,是因为反射吗?不过blockerHandler应该不用反射吧,,直接调用似乎可以,,
-
fallback /fallbackClass: fallback 指定的函数负责处理业务运行的异常,可选项,fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数默认需要和原方法在同一个类中,通过指定 fallbackClass 为对应类的 Class 对象,则可以指定指定为其他类的函数,但注意对应的函数必需为 static 函数,否则无法解析。
fallback 函数的返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
@SentinelResource 的 fallback 负责业务运行的异常,blockHandler 负责 sentinel 配置的违规。
流量控制(限流)
原理是监控应用流量的 QPS 或 并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被顺势的流量高峰冲垮,从而保障应用的高可用性;
- Q: 怎么控制
- A: dashboard设置规则时选择
高级选项-流控模式
:- 直接 返回Blocked by Sentinel
- 关联
- 链路
- A: dashboard设置规则时选择
流控模式 三种
配置资源:在编辑规则时设置的资源
关联资源:和配置资源相关联的资源
直接
当对配置资源的请求超过阈值时,对配置资源的请求的处理(三种处理方式在本文下一小节)
关联
当所关联的资源的请求超过阈值后,对当前资源进行的处理
如在以下例子中,当关联的"/aa"资源访问次数超过阈值,则"/demo"资源要受到流控处理
链路
对链路上的资源的处理
流控效果/处理方式 三种
注意:只适用于QPS限流
- 快速失败
- 直接拒绝请求并抛出异常
FlowExcrption
- 直接拒绝请求并抛出异常
- Warm up
- 冷启动,预热
- 在一定时间(
预热时长
)内将系统逐渐拉至高水位
- 排队等待 Rate Limiter
- 同Warm up相比,特点为始终匀速处理
注意:这一效果只针对QPS流控,并发线程数流控不支持。
- 同Warm up相比,特点为始终匀速处理
排队等待的方式会以匀速排队方式严格控制请求通过的间隔时间,也就是让请求以均匀的速度通过,其余的排队等待,它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
对应的是漏桶算法,源码对应得类:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。该方式的作用如下图:
图片该方式主要用于处理间隔性突发的流量。假设某时刻来了大流量的请求,如果此时要处理所有请求,很可能会导致系统负载过高,影响稳定性。
但其实接下来几秒可能系统处于空闲状态,若直接把多余的请求丢弃则没有充分利用系统的处理能力,所以我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
Sentinel 的 Rate Limiter 模式能在某一段时间间隔内以匀速方式处理这样的请求, 充分利用系统的处理能力, 也就是削峰填谷, 保证资源的稳定性。
Sentinel会以固定的间隔时间让请求通过, 访问资源,当请求到来时,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间大于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过;反之,则马上抛出阻塞异常。
下图能很形象的展示这种场景的削峰填谷的作用:X轴代表时间,Y轴代表系统处理的请求
结合Gateway
Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流。
在Gateway模块中添加依赖:
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
限流
在Gateway配置类
中添加限流规则,注册全局SentinelGatewayFilter()过滤器,
以一分钟访问超过三次则限流为例:
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
/**
* 网关限流配置
*
*/
@Configuration
public class GatewayConfig
{
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter()
{
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit()
{
// 加载网关限流规则
initGatewayRules();
}
/**
* 网关限流规则
*/
private void initGatewayRules()
{
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("ruoyi-system")
.setCount(3) // 限流阈值
.setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
// 加载网关限流规则
GatewayRuleManager.loadRules(rules);
}
}
- 这种小事就不能交给配置文件吗?难道因为不是gateway自己的?
- 为什么这里不用控制面板了
- 那个异常处理可以在配置文件,详见“自定义异常”
分组限流
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.ruoyi.gateway.handler.SentinelFallbackHandler;
/**
* 网关限流配置
*
*/
@Configuration
public class GatewayConfig
{
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
return new SentinelFallbackHandler();
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter()
{
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit()
{
// 加载网关限流规则
initGatewayRules();
}
/**
* 网关限流规则
*/
private void initGatewayRules()
{
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("system-api")
.setCount(3) // 限流阈值
.setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
rules.add(new GatewayFlowRule("code-api")
.setCount(5) // 限流阈值
.setIntervalSec(60));
// 加载网关限流规则
GatewayRuleManager.loadRules(rules);
// 加载限流分组
initCustomizedApis();
}
/**
* 限流分组
*/
private void initCustomizedApis()
{
Set<ApiDefinition> definitions = new HashSet<>();
// ruoyi-system 组
ApiDefinition api1 = new ApiDefinition("system-api").setPredicateItems(new HashSet<ApiPredicateItem>()
{
private static final long serialVersionUID = 1L;
{
// 匹配 /user 以及其子路径的所有请求
add(new ApiPathPredicateItem().setPattern("/system/user/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}
});
// ruoyi-gen 组
ApiDefinition api2 = new ApiDefinition("code-api").setPredicateItems(new HashSet<ApiPredicateItem>()
{
private static final long serialVersionUID = 1L;
{
// 只匹配 /job/list
add(new ApiPathPredicateItem().setPattern("/code/gen/list"));
}
});
definitions.add(api1);
definitions.add(api2);
// 加载限流分组
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
}
- 不过这样的话,,路径就写死在代码里了
注解方式
sentinel 不仅支持通过硬代码方式进行资源的申明,还能通过注解方式进行声明,为了让注解生效,还需要配置切面类SentinelResourceAspect
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
自定义异常
为了展示更加友好的限流提示, Sentinel支持自定义异常处理。
方案一:yml
配置
# Spring
spring:
cloud:
sentinel:
scg:
fallback:
mode: response
response-body: '{"code":403,"msg":"请求超过最大数,请稍后再试"}'
方案二:GatewayConfig
注入Bean
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
return new SentinelFallbackHandler();
}
SentinelFallbackHandler.java
import java.nio.charset.StandardCharsets;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
/**
* 自定义限流异常处理
*
*/
public class SentinelFallbackHandler implements WebExceptionHandler
{
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
{
if (exchange.getResponse().isCommitted())
{
return Mono.error(ex);
}
if (!BlockException.isBlockException(ex))
{
return Mono.error(ex);
}
return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
{
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}
实现原理
在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)
这些功能插槽有些类似游戏或是组装电脑里“升级插槽”的概念,在原有基础上添加功能。可以自定义插槽
插槽介绍
NodeSelectorSlot
这个 slot 主要负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级。
资源确实是树状结构的,就像树形目录。结构上资源和url很像,也应当是树形结构
熔断降级设计理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。
缺点:
- 增加了线程切换的成本
- 需要预先给各个资源做线程池大小的分配,容易导致碎片化
- 怎么线程池隔离?不如说实际上隔离时怎么做到的?内存地址拒绝访问?
Sentinel 对这个问题采取了两种手段:
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护
Sentinel 同时对系统的维度提供保护。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
进一步学习
相关概念:
SPI:SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,比如AT91RM9200。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
附录
@SentinelResource注解参数
① value: 资源名称,必需项
② entryType: entry 类型,可选项(默认为 EntryType.OUT)
⑤ defaultFallback(since 1.6.0): 默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑。defaultFallback 函数默认需要和原方法在同一个类中,通过指定 fallbackClass 为对应类的 Class 对象,则可以指定指定为其他类的函数,但注意对应的函数必需为 static 函数,否则无法解析。defaultFallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback和 defaultFallback,则只有 fallback会生效。
defaultFallback函数的返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
⑥ exceptionsToIgnore(since 1.6.0): 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)
流控规则
一条限流规则由以下属性组成:
① resource: 资源名,即限流规则的作用对象,默认请求路径。
② limitApp: 流控针对的调用来源,若为 default 则不区分调用来源,默认值default
③ count: 限流阈值
④ grade: 限流阈值类型(1代表 QPS,0 代表并发线程数),默认值QPS
⑤ strategy: 流控模式
直接拒绝(默认): 接口达到限流条件时,直接限流
关联: 当关联的资源达到阈值时,就限流自己(适合做应用让步)
链路: 只记录指定链路上的流量,指定资源从入口资源进来的流量,如果达到阈值,就可以限流
⑥ controlBehavior: 流控效果
快速失败(默认): 当 QPS 超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
排队等待: 这种方式严格控制了请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
Warm Up: 该方式主要用于系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮的情况。预热底层是根据令牌桶算法实现的。
以上几种属性在 sentinel-dashboard 控制台对应的规则如下图:
Spring事务失效
在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是 public,则TransactionAttribute返回 null,即不支持事务。
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
也就是说,如果我们自定义的事务方法(即目标方法),它的访问权限不是public,而是 private、default 或 protected 的话,spring 则不会提供事务功能。
2. 方法用 final 修饰
spring 事务底层使用了 aop,也就是通过 jdk 动态代理或者 cglib,帮我们生成了代理类,在代理类中实现的事务功能。
但如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
注意:如果某个方法是 static 的,同样无法通过动态代理,变成事务方法。
- 但是sentinel却没提static和final,,如果只是私有方法,那么可能是其他类判断方法类型的,,为什么?是为了在反射的情况下仍然能保证访问权限吗?
控制台模式存在的问题
优点是方便。缺点是:
通过 Sentinel-Dashboard 控制台配置的规则依然要推送到微服务应用中 Sentinel 客户端本身才能生效,而微服务之间的调用链路等指标信息也需要推送给Sentinel控制台,才能比较方便地使用Sentinel提供的一些能力,因此在开源的架构版本中需要微服务应用本身开启独立端口与 sentinel-dashboard 进行通信,从而获取配置规则以及上送微服务应用的各类指标信息。
而这显然也会占用微服务额外的资源,并且由于 sentinel-dashboard 在此条件下并不具备集群部署能力,因此也会形成一个单节点问题。
在开源版本架构中,通过 sentinel-dashboard 控制台配置的限流、熔断降级等规则都是存储于 Sentinel-Dashboard 控制台服务的内存之中,如果控制台服务重启或者微服务应用重启都会导致规则丢失,而这在生产环境下显然是不能接受的,因此 Sentinel 官方推荐在生产架构中使用第三方数据源作为永久存储中心,比如 nacos、apollo、zookeeper,这样各个微服务的限流、降级规则都可以永久存储。
sentinel 整合 openFeign:
上文介绍的都是在单个模块间的进行 fallback 和 blockhandler 测试,但在实际的 SpringCloud 微服务开发场景中肯定会遇到服务间远程服务调用的问题,而目前最主流的远程调用组件就是 openFeign 了,那接下来我们看看 sentinel 如何整合 openFeign 进行熔断降级。
1、引入 openFeign 的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、配置文件中开启 openFeign 对 sentinel 熔断降级的支持:
feign.sentinel.enabled=true
3、消费者主启动类添加注解 @EnableFeignClients 激活 openFeign
4、消费者声明接口,标识要调用提供方模块的哪个方法,并在 @FeignClient 注解中使用 fallback 属性指定熔断降级类:
@FeignClient(value = "payment-provider", fallback = PaymentServiceImpl.class)
public interface PaymentService {
@GetMapping("/payment/get/{id}")
public CommonResult paymentSql(@PathVariable("id")Long id);
}
5、添加降级类:实现第4步的定义的接口,并添加降级逻辑:
@Component
public class PaymentServiceFallback implements PaymentService {
@Override
public CommonResult paymentSql(Long id) {
return new CommonResult(414, "open-feign 整合 sentinel 实现的全局服务降级策略",null);
}
}
PaymentServiceFallback 是 PaymentService 的降级回调类,一旦PaymentService 中对应得接口出现了异常,则会调用PaymentServiceFallback类中对应得方法进行降级处理。
- 为什么fallback指定的是ServiceImpl的类?
- 没看出和Sentinel有什么关系?
- feign不是还默认集成了Hystrix吗