why
在分布式中,为了保证服务高可用,就必须对请求进行限流或服务降级的方式才能够保证不会被流量拖垮导致雪崩效应,
what–什么是sentinel?
它是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、服务降级、系统负载等多个维度来帮助我们保障微服务的稳定性。
特性:
- 提供实时监控的功能,包括查看单机秒级数据,设置500台以下规模的集群汇总运行情况
- 只需引入maven依赖,即可快速与Spring Cloud、Dubbo、gRPC等整合
名词解释
- 限流:假设某个系统可以最大可以接受1万的并发,但这一时刻并发数是2万,那么限流就是保证1万用户是正常使用的,否则请求数量过大,可能会拖垮系统。
- 熔断:熔断指的是当某个服务提供者无法正常为调用者提供服务时,如请求超时、服务异常,为了防止整个系统发生雪崩效应,暂时将出现故障的接口隔离出来,断开接口与外部的联系。此时调用者调用此服务会直接失败,直到服务恢复正常为止。
服务降级的几种常见方案
- 平均响应时间:比如1秒内进入5个请求,对应时刻的平均响应时间超过阈值,那么接下来一个固定时间窗口内,对这个方法的访问都会自动熔断。
- 异常比例:调用某个方法每秒的异常总数比例超过设定的阈值,则该资源会自动进入降级状态,也就是在接下来一段时间窗口中,对这个方法的调用都会自动返回。
- 异常数量:类似于异常比例,这里是指某个方法在指定时间窗口内的异常数量达到阈值,则会触发熔断
常用的四种限流算法
how–Quick Start
接下来我们通过一个示例来了解下sentinel能做什么
-
新建一个maven项目,并引入sentinel依赖
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.7.1</version> </dependency>
-
编写一个最简Demo
public class SentinelMinimalTest { public static void main(String[] args) { initFlowRules(); while (true) { Entry entry = null; try { entry = SphU.entry("HelloWorld"); // 业务处理方法 process(); } catch (BlockException e) { /*流控处理开始*/ System.out.println("block"); /*流控处理结束*/ } finally { if (entry != null) { entry.exit(); } } } } private static void process() { /*业务的的开始*/ System.out.println("HelloWorld"); /*业务逻辑结束*/ } private static void initFlowRules() { List<FlowRule> flowRules = new ArrayList<>(); FlowRule rule = new FlowRule(); // 设置需要保护的资源。这个资源的名称必须和SphU.entry中使用的名称保持一致。 rule.setResource("HelloWorld"); /** * 限流阈值类型: * * 并发线程数模式——RuleConstant.FLOW_GRADE_THREAD * * QPS模式——RuleConstant.FLOW_GRADE_QPS */ rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 设置速率为20qps rule.setCount(20); // 是否需要针对调用来源进行限流,默认是default,即:不区分调用来源 rule.setLimitApp("default"); /** * 调用关系限流策略: * * 直接——RuleConstant.STRATEGY_DIRECT (默认) * * 关联——RuleConstant.STRATEGY_RELATE * * 链路——RuleConstant.STRATEGY_CHAIN */ rule.setStrategy(RuleConstant.STRATEGY_DIRECT); /** * QPS流量流控行为: * * 直接拒绝——CONTROL_BEHAVIOR_DEFAULT 0 (默认) * * 慢启动模式——CONTROL_BEHAVIOR_WARM_UP 1 * * 匀速排队——CONTROL_BEHAVIOR_RATE_LIMITER 2 * * 冷启动+匀速排队——CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER 3 */ rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 是否是集群限流,默认为否 rule.setClusterMode(false); flowRules.add(rule); FlowRuleManager.loadRules(flowRules); } /** * 配置熔断策略 */ public void initDegradeRule() { List<DegradeRule> rules = new ArrayList<>(); DegradeRule rule = new DegradeRule(); rule.setResource("process"); // 限流阈值 rule.setCount(10); /** * 熔断策略,支持秒级RT、秒级异常比例、分钟及异常数。默认是秒级RT。 * * 平均响应时间——RuleConstant.DEGRADE_GRADE_RT 0 * * 异常比例——RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO 1 * * 异常数——RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT 2 */ rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); // 熔断降级的时间窗口,单位为秒。也就是触发熔断降级之后多长时间内自动熔断。 rule.setTimeWindow(10); // 触发的异常熔断最小请求数,请求数小于该值时,即使异常比例超出阈值,也不会触发熔断,默认值是5 rule.setMinRequestAmount(5); rules.add(rule); } }
基本过程:
- 定义资源:SphU.entry & SphO.entry
- 定义规则:FlowRule rule = new FlowRule();
- 查看运行结果:
${user_home}/logs/csp/sentinel-block.log
-
Demo 运行之后,我们可以在日志
${user_home}/logs/csp/sentinel-block.log
里看到下面的输出:|--timestamp-|------date time----|-resource-|p |block|s |e|rt 1529998904000|2018-06-26 15:41:44|HelloWorld|20|0 |20|0|0 1529998905000|2018-06-26 15:41:45|HelloWorld|20|5579 |20|0|728 1529998906000|2018-06-26 15:41:46|HelloWorld|20|15698|20|0|0 1529998907000|2018-06-26 15:41:47|HelloWorld|20|19262|20|0|0 1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0 1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0
其中 p 代表通过的请求, block 代表被阻止的请求, s 代表成功执行完成的请求个数, e 代表用户自定义的异常, rt 代表平均响应时长。
可以看到,这个程序每秒稳定输出 “hello world” 20 次,和规则中预先设定的阈值是一样的。
-
启动 Sentinel 控制台,jar包下载地址:https://github.com/alibaba/Sentinel/releases ,启动命令:
java -Dserver.port=8000 -Dcsp.sentinel.dashboard.server=localhost:8000 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
在启动之后,返回控制台地址:http://localhost:8000/,账户密码: sentinel/sentinel
可以实时监控各个资源的运行情况,并且可以实时地修改限流规则
Sentinel与Spring Cloud集成
Sentinel提供了一系列starter来支持Spring Cloud的集成,接下来我们详细看下
-
引入如下sentinel依赖
<!--spring cloud 集成方式--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.1.1.RELEASE</version> </dependency> <!--注解方式配置限流--> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.4.0</version> <exclusions> <exclusion> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> </exclusion> </exclusions> </dependency>
-
编写Configuration配置类来配置一个Aspect,作用是切面方法来获取调用次数
@Configuration public class SentinelAspectConfiguration { @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } }
-
编写服务类来指定一个资源并配置相关handler
@Service public class SentinelWithAnnotationServiceImpl { /** * 业务处理逻辑 */ @SentinelResource(value = "annotation", blockHandler = "processBlockHandler", fallback = "fallbackHandler") public void process() { System.out.println("annotation!"); /*您的业务逻辑*/ } /** * Block异常处理函数,参数最后多一个BlockException,其余与原函数一致. */ public void processBlockHandler(BlockException e) { System.out.println("block handler!"); } /** * Fallback函数,函数签名与原函数一致或加一个Throwable类型的参数. */ public void fallbackHandler() { System.out.println("fallback handler!"); } }
这里我们使用
@SentinelResource
注解来配置了一个annotation
的资源,同时配置了限流后的handlerprocessBlockHandler
以及回退函数fallbackHandler
-
编写一个限流规则
/** * 限流策略配置,通过SPI的方式提供服务,具体查看:META-INF\services\com.alibaba.csp.sentinel.init.InitFunc */ public class FlowRuleStrategy implements InitFunc { /** * 配置限流规则 * * @throws Exception */ @Override public void init() throws Exception { List<FlowRule> rules = new ArrayList<>(); FlowRule rule1 = new FlowRule(); rule1.setCount(1); rule1.setResource("annotation"); rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); rule1.setLimitApp("default"); rules.add(rule1); FlowRuleManager.loadRules(rules); } }
这里是annotation资源限流为1秒一次,超过则限流,会调用blockHandler()方法。
配置自动加载文件:
文件内容为刚才我们写得规则类:com.siqi.sentinel.sentineldemo.strategy.FlowRuleStrategy
-
编写一个controller,方便我们测试触发限流规则
@RestController public class SentinelDemoController { @Resource private SentinelWithAnnotationServiceImpl sentinelWithAnnotationService; /** * 注解配置方式实现限流,每秒只能访问一次 * * @throws Exception */ @GetMapping("/annotation") public String annotation() throws Exception { sentinelWithAnnotationService.process(); return "annotation"; } }
-
在application.yml配置文件中配置:
spring: application: name: sentinel-demo cloud: sentinel: transport: dashboard: 127.0.0.1:8000
-
运行项目,并访问http://localhost:8080/annotation,查看控制台,发现每秒仅通过了一个请求,其余的都触发了blockHandler,说明被限流了
至此,Sentinel 与 Spring Cloud 集成完毕。
Sentinel 集成 Nacos
-
启动Nacos:
sh startup.sh -m standalone
,以单机方式启动 -
打开控制台:http://localhost:8848/nacos,用户名&密码为: nacos & nacos
-
在nacos控制台中创建流量配置规则:
[ { "resource": "/withnacos", "limitApp": "default" , "grade": 1, "count": 5, "strategy": 0, "clusterMode": false } ]
-
创建一个
application-nacos.yml
配置文件并使用此配置文件启动spring: application: name: sentinel-nacos-demo cloud: sentinel: transport: dashboard: 127.0.0.1:8000 datasource: - nacos: # 配置nacos server-addr: 127.0.0.1:8848 data-id: ${spring.application.name}-sentinel group-id: DEFAULT_GROUP data-type: json rule-type: flow
-
为了便于测试,我们在controler类中增加一个get方法:
/** * 基于nacos实现规则配置 */ @GetMapping("/withnacos") public String withnacos() { return "-----withnacos-----"; }
-
访问地址:http://localhost:8080/withnacos ,观察控制台发现,限流情况完全是按照nacos中配置的限流规则进行的。
通过nacos,我们可以实时在在线修改限流规则,还可以将降级规则也配置在nacos进行管理
扩展
-
如何自定义一个全局的限流异常处理?
@Service public class MyBlockHandler implements UrlBlockHandler { /** * block处理,公共handler */ @Override public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { httpServletResponse.setHeader("Content-Type", "application/json;charset=UTF-8"); String message = "{\n" + "\t\"code\":\"400\",\n" + "\t\"message\":\"请求已被限流!\"\n" + "}"; httpServletResponse.getWriter().write(message); } }
当请求被限流后,会返回message
总结
除了文中提到阿里的sentinel,还有Hystrix、resilience4j,有兴趣可以了解。
功能对比:
功能 | Sentinel | Hystrix | resilience4j |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
流量整形 | 支持预热模式、匀速器模式、预热排队模式(流量规则处可配置) | 不支持 | 简单的 Rate Limiter 模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等 | 简单的监控查看 | 不提供控制台,可对接其它监控系统 |