Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。这里你们可能会问:Sentinel 和以前经常使用的熔断降级库 Netflix Hystrix 有什么异同呢?Sentinel官网有一个对比和Hystrix迁移到sentinel的文章,这里摘抄一个总结的表格,具体的对比能够点此 连接 查看。 java
功能对比
从对比的表格能够明显看到,Sentinel比Hystrix在功能性上还要强大一些。git
Sentinel 功能主要体如今三个方面github
对于系统来讲,任意时间到来的请求每每是随机不可控的,而系统的处理能力是有限的。咱们须要根据系统的处理能力对流量进行控制。 web
控制角度以下:spring
当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而致使级联故障。手段以下json
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防御中重要的一环。当系统负载较高的时候,若是还持续让请求进入,可能会致使系统崩溃,没法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。若是 这个时候其它的机器也处在一个边缘状态的时候,这个增长的流量就会致使这台机器也崩溃,最后致使整个集群不可用。api
针对这个状况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围以内处理最多的请求。markdown
这里我使用sentinel 是基于gradle配置,兼容spring clould alibaba,因此添加以下依赖网络
compile'com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.1.0.RELEASE' compile group: 'com.alibaba.csp', name: 'sentinel-transport-simple-http', version: '1.6.3'
Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException等,固然也支持使用aop的方式,这里演示使用aop的方式,添加以下配置类架构
@Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect();
}
}
@SentinelResource
用于定义资源,并提供可选的异常处理和 fallback 配置项 。@SentinelResource 注解包含如下属性
服务具体实现类
@Service @Slf4j public class HelloProviderServiceImpl implements HelloProviderService { @Autowired private ConfigurableEnvironment configurableEnvironment; // 对应的 `handleException` 函数须要位于 `ExceptionUtil` 类中,而且必须为 static 函数 @Override @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = { ExceptionUtil.class}) public void test() { log.info("Test"); } @Override @SentinelResource(value = "sayHi", blockHandler = "exceptionHandler", fallback = "helloFallback") public String sayHi(long time) { if (time < 0) { throw new IllegalArgumentException("invalid arg"); } try { Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalArgumentException("inter arg"); } return String.format("Hello time %d", time); }
// 这里俗称资源埋点,在设置限流策略的时候会根据此埋点来控制 @Override @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback", exceptionsToIgnore = {IllegalStateException.class}) public String helloAnother(String name) { if (name == null || "bad".equals(name)) { throw new IllegalArgumentException("oops"); } if ("foo".equals(name)) { throw new IllegalStateException("oops"); } return "Hello, " + name; } // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String helloFallback(long s, Throwable ex) { log.error("fallbackHandler:" + s); return "Oops fallbackHandler, error occurred at " + s; } //默认的 fallback 函数名称 public String defaultFallback() { log.info("Go to default fallback"); return "default_fallback"; } // Block 异常处理函数,参数最后多一个 BlockException,其他与原函数一致. public String exceptionHandler(long s, BlockException ex) { // Do some log here. return "Oops,exceptionHandler, error occurred at " + s; } }
服务接口
public interface HelloProviderService { public String sayHi(long t) throws InterruptedException; String helloAnother(String name); void test(); }
ExceptionUtil类
@Slf4j public final class ExceptionUtil { public static void handleException(BlockException ex) { log.info("Oops: " + ex.getClass().getCanonicalName()); } }
controller 类
@RestController @Slf4j public class HelloProviderController { @Autowired HelloProviderServiceImpl helloServiceProviderService; @GetMapping("/sayHi") public String sayHi(@RequestParam(required = false) Long time) throws Exception { if (time == null) { time = 300L; } helloServiceProviderService.test();
return helloServiceProviderService.sayHi(time); } @GetMapping("baz/{name}") public String apiBaz(@PathVariable("name") String name) { return helloServiceProviderService.helloAnother(name); } }
一个轻量级的开源控制台,它提供机器发现以及健康状况管理、监控(单机和集群),规则管理和推送的功能。主要能够经过该控制台对服务端设置的资源埋点进行动态的限流配置推送,这样能够灵活的设置限流策略而不用在代码里写死
平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)以内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算做 4900 ms,若须要变动此上限能够经过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5,而且每秒异常总数占经过量的比值超过阈值(DegradeRule 中的 count)以后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)以内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],表明 0% - 100%。
异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值以后会进行熔断。注意因为统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
能够启用Sentinel 控制台,在控制台上直接配置熔断降级规则。
Feign是Netflix公司开源的轻量级的一种负载均衡的HTTP客户端,,使用Feign调用API就像调用本地方法同样,从避免了 调用目标微服务时,须要不断的解析/封装json 数据的繁琐。 Spring Cloud引入Feign而且集成了Ribbon实现客户端负载均衡调用。 通俗一点讲:能够像调用本地方法同样的调用远程服务的方法。
固然其中也有很多坑等踩。
Sentinel 适配了 Fegin组件。若是想使用,除了引入 spring-cloud-starter-alibaba-sentinel
的依赖外还须要 2 个步骤:
配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true
openfeign starter
依赖使 sentinel starter
中的自动化配置类生效:compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '2.1.3.RELEASE'
添加接口 EchoService类,该接口经过@FeignClient(name = "service-provider")注解来绑定该接口对应service01服务
@FeignClient(name = "nacos-provider-sentianel1", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class) public interface EchoService { @GetMapping(value = "/sayHi") String sayHi(@RequestParam(value = "time", required = false) Long time); @RequestMapping("/api/{name}") String apiBaz(@PathVariable("name") String name); }
其中 @FeignClient 中name 中的值做为 提供服务提供方的名称,该接口中配置当前服务须要调用nacos-provider-sentianel1服务提供的接口。nacos-provider-sentianel1注册到注册服务上,我这里使用的是Nacos.
服务配置以下
nacos-provider-sentianel1 中的controller是这个样子的,这里能够看到 和EchoService中的方法签名都是一致的
@RestController
public class HelloProviderController2 { @GetMapping("/echo") public String helloConsumer(@RequestParam(required = false) Long time) { return "echo"; } @GetMapping("/api/{name}") public String apiBaz(@PathVariable("name") String name) { return "another provider " + name; } }
添加 EchoServiceFallback,这里是fegin的Fallback机制,主要用来作容错处理。由于
在网络请求时,可能会出现异常请求,若是还想再异常状况下使系统可用,那么就须要容错处理。
@Component。 public class EchoServiceFallback implements EchoService { @Override public String sayHi(Long time) { return "sayHi fallback"; } @Override public String apiBaz(String name) { return "apiBaz fallback"; } }
添加FeignConfiguration
@Configuration
public class FeignConfiguration { @Bean public EchoServiceFallback echoServiceFallback() { return new EchoServiceFallback(); } }
在上文HelloProviderServiceImpl的基础上添加EchoService调用
@Service @Slf4j public class HelloProviderServiceImpl implements HelloProviderService { @Autowired private ConfigurableEnvironment configurableEnvironment; @Autowired EchoService echoService; // 对应的 `handleException` 函数须要位于 `ExceptionUtil` 类中,而且必须为 static 函数 @Override @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = { ExceptionUtil.class}) public void test() { log.info("Test"); } @Override @SentinelResource(value = "sayHi", blockHandler = "exceptionHandler", fallback = "helloFallback") public String sayHi(long time) { if (time < 0) { throw new IllegalArgumentException("invalid arg"); } try { Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalArgumentException("inter arg"); } return String.format("Hello time %d", time); } @Override @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback", exceptionsToIgnore = {IllegalStateException.class}) public String helloAnother(String name) { if (name == null || "bad".equals(name)) { throw new IllegalArgumentException("oops"); } if ("foo".equals(name)) { throw new IllegalStateException("oops"); } return "Hello, " + name; } // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数. public String helloFallback(long s, Throwable ex) { log.error("fallbackHandler:" + s); return "Oops fallbackHandler, error occurred at " + s; } //默认的 fallback 函数名称 public String defaultFallback() { log.info("Go to default fallback"); return echoService.apiBaz("bad"); //return "default_fallback"; } // Block 异常处理函数,参数最后多一个 BlockException,其他与原函数一致. public String exceptionHandler(long s, BlockException ex) { // Do some log here. return "Oops,exceptionHandler, error occurred at " + s; } }
这里咱们在defaultFallback中使用 echoService.apiBaz("bad") 来调用nacos-provider-sentianel1 的apiBaz方法
在sentinel控制台中配置helloAnother的降级规则,当触发降级后,将会调用acos-provider-sentianel1服务的apiBaz方法,返回结果。
使用sentinel控制系统流量,当系统流超出当前服务的接受范围的时候,能够经过Feign 调用降级服务,这样就可构成一个最基础的熔断降级模块,固然Feign中还集成了Ribbon,能够经过配置实现客户端负载均衡调用。