1. 概念解释
限流:对并发访问进行限速。
限流的一些行为: 1. 拒绝服务:将多余的请求直接拒绝掉
2.服务降级:降级甚至关闭后台的某些服务
3.特权请求:在多租户或者对用户进行分级时,考虑让特权用户进行访问
4.延时处理:可以利用队列把请求进行缓存
熔断:在分布式系统中,往往需要依赖下游服务,不管是内部系统还是第三方服务。如果下游服务出现问题,此时还盲目请求的话,会失败很多次还是会傻傻去请求,去等待,增加了整个链路的请求时间。
熔断模式可以防止应用程序不断尝试可能超时和失败的服务,能达到应用程序执行而不必等待下游服务修正错误。熔断模式最牛在于能让应用程序自我诊断下游系统的错误是否已经修正,如果没有,不放量去请求,如果请求成功了,慢慢增加请求,再次尝试调用。比如说A服务调用B服务,B服务是下游的服务提供,或者是第三方服务,容易发生问题。这样既能防止不断的调用,是下游服务更坏,保护了下游方,还能降低自己的执行成本,快速的响应,减少延迟,增加吞吐量。
降级:为了解决资源不足和访问量增加的矛盾。在有限的资源情况下,为了能抗住大量的请求,就需要对系统做一些牺牲,放弃一些功能,来保证整个系统正常运行。
2. 常见限流算法
- 静态窗口限流:限制每秒内请求的数量
- 动态窗口限流:限制当前时间1s内请求的数量。例如当前是2.5秒,静态:统计第2秒到现在的请求数;动态:统计第1.5秒到现在的请求数
- 漏桶限流:将请求全部放入桶中,使用队列的形式进行消费,会控制消费的速度。(进来的时候可以大流量地接收,出去的时候可以匀速出去)
- 令牌桶限流:令牌桶中存放的是令牌,会有一个产生令牌的程序,每秒产生多少个令牌,放入桶中,如果满了,则不再放了。取令牌:每过来一个请求,就取一个令牌,如果取不到令牌,则该请求就会失败
- 令牌大闸:在令牌桶的基础上进行控制,一次性产生N个令牌,产生完之后便不再生成。
3. Sentinel基础使用
限流是作用于被调用方,熔断是作用于调用方
Sentinel的使用主要包含两部分:
- 核心库:不依赖任何框架/库,能够运行于java8以及以上的版本的运行环境
- 控制台:主要负责管理推送规则、监控、管理机制消息等。
Sentinel控制台(github主页有)是一个标准的Springboot应用,以SpringBoot的形式来运行jar包即可,该程序以8080端口运行,打开页面之后账户和密码均为sentinel
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
程序中引入sentinel
- 引入依赖:使用的是Spring-Cloud-alibaba
<!-- 限流熔断 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 定义资源:
为某个接口加入注解
@SentinelResource("doConfirm")
public void doConfirm(ConfirmOrderDoReq req){XXX};
- 定义规则:使用代码的方式定义规则
在该模块中定义规则并进行启动
@SpringBootApplication
@MapperScan("com.monster.business.mapper")
@ComponentScan("com.monster")
@EnableFeignClients("com.monster.business.feign")
@EnableCaching
public class BusinessApplication {
private static final Logger LOG= LoggerFactory.getLogger(BusinessApplication.class);
public static void main(String[] args) {
SpringApplication app=new SpringApplication(BusinessApplication.class);
Environment env=app.run(args).getEnvironment();
LOG.info("启动成功!");
LOG.info("地址:\thttp://127.0.0.1:{}{}",env.getProperty("server.port"),env.getProperty("server.servlet.context-path"));
initFlowRules();
LOG.info("已定义限流规则");
}
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("doConfirm");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
此时QPS为1,每秒只允许1个请求到达。
所以其他线程的请求就会得到sentinel报出的错误,但是这样不太正常,可以省略这些报错,相应处理如下:
//当被拦截的请求不会执行doConfirm方法,但会去执行doConfirmBlock
@SentinelResource(value="doConfirm",blockHandler = "doConfirmBlock")
public void doConfirm(ConfirmOrderDoReq req){XXX};
doConfirmBlock方法的定义(返回值也需要一致),参数是有限制的。参数需要和doConfirm方法参数一样的基础上,再加一个BlockException e
public void doConfirmBlock(ConfirmOrderTicketReq req, BlockException e){
LOG.info("购票请求被限流:{}",req);
throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION);
}
4. Sentinel控制台&&使用sentinel进行限流
应用与控制台进行交互:在yml文件中进行配置
spring:
cloud:
sentinel:
transport:
port: 8719 //sentinel启动的固定端口
dashboard: localhost:8080 //sentinel-dashboard启动的端口
可以在簇点链路中加入流控规则
但是控台不保存规则,一旦应用重启,之前设置的规则就全部消失
5. Sentinel+Nacos实现限流规则持久化
- 引入依赖
<!-- sentinel + nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- 在Business模块增加配置,客户端和nacos做一个关联
下面的属性需要与nacos中的配置一一对应
spring.cloud.sentinel.datasource.nacos.nacos.serverAddr=127.0.0.1:8848
spring.cloud.sentinel.datasource.nacos.nacos.namespace=train
spring.cloud.sentinel.datasource.nacos.nacos.groupId=TRAIN_GROUP
spring.cloud.sentinel.datasource.nacos.nacos.dataId=sentinel-business-flow
spring.cloud.sentinel.datasource.nacos.nacos.ruleType=flow
- 配置nacos
6. Sentinel限流不同的流控效果-Warm Up
默认为快速失败,还有Warm up和排队等待
Warm up效果:coldFactor即为请求QPS从(阈值/3)开始,经过多少预热时长才逐渐升值设定的QPS阈值,比如阈值是100,时长为10秒,则从33开始经过10秒上升到100
设置一个测试类
@RestController
public class TestController {
@SentinelResource("hello")
@GetMapping("/hello")
public String hello()throws InterruptedException{
return "Hello World! Business!";
}
}
改变配置文件
spring.cloud.sentinel.datasource.flow.nacos.serverAddr=127.0.0.1:8848
spring.cloud.sentinel.datasource.flow.nacos.namespace=train
spring.cloud.sentinel.datasource.flow.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.flow.nacos.dataId=sentinel-business-flow
#flow表示限流
spring.cloud.sentinel.datasource.flow.nacos.ruleType=flow
在Nacos中配置一个名为sentinel-business-flow 的配置文件
[
{
// 资源名称
"resource": "hello",
//针对来演
"limitApp": "default",
// 按照QPS模式
"grade": "1",
//阈值为10
"count": 10,
// 策略为直接放弃
"strategy": 0,
// 流控模式为1即Warm Up
"controlBehavior": 1,
// 预热时长为2s
"warmUpPeriodSec": 2,
// 集群模式
"clusterMode": false
}
]
一开始的QPS为3,然后慢慢增长到10。这就是预热的效果
7. Sentinel限流不同的流控效果-排队等待
排队等待需要设置一个排队等待时间,默认为500ms。
接收到的(超过阈值的)请求,会进行排队等待,如果在500ms内能够接收请求的话就处理,否则就拒绝
相应的配置文件内容为:设置超时时间为1000ms即1s
[
{
"resource": "hello",
"limitApp": "default",
"grade": "1",
"count": 10,
"strategy": 0,
"controlBehavior": 2,
"maxQueueingTimeMs": 1000,
"clusterMode": false
}
]
设置20个线程去请求,理论上来说是第一秒处理10个请求,然后10个请求进行排队等待,在第二秒进行处理
8. Sentinel+Feign熔断
调用方:Batch模块
被调用方:Business模块的Hello接口
当被调用方不稳定时,调用方会启动备用方案,这就是熔断
为Batch模块加入相关依赖,并进行配置,degrade表示熔断降级的意思
spring.cloud.sentinel.transport.port=8719
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.datasource.degrade.nacos.serverAddr=127.0.0.1:8848
spring.cloud.sentinel.datasource.degrade.nacos.namespace=train
spring.cloud.sentinel.datasource.degrade.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.degrade.nacos.dataId=sentinel-batch-degrade
#flow表示限流
spring.cloud.sentinel.datasource.flow.nacos.ruleType=degrade
# sentinel默认不监控feign,需要配置
feign.sentinel.enabled=true
# 上面改为true后,启动会报注入错误,需要改成懒加载
spring.cloud.openfeign.lazy-attributes-resolution=true
Batch模块远程调用代码为
@Resource
BusinessFeign businessFeign;
@GetMapping("/hello")
public String hello() {
String businessHello = businessFeign.hello();
LOG.info(businessHello);
return "Hello World! Batch! " + businessHello;
}
Batch的nacos配置代码为:
[{
"resource": "GET:http://business/business/hello",
"grade": 0,
"count": 201,
"timeWindow": 11,
"minRequestAmount": 6,
"statIntervalMs": 1000,
"slowRatioThreshold": 0.3
}]
对应的熔断规则如下图所示:
最大RT:表示响应时间,如果响应时间大于201ms,当有百分之三十的请求响应时间大于201ms时出发熔断
熔断时长:表示触发熔断之后,后面的11秒所有的请求会抛出
最小请求数:一秒内必须要超过6个请求时,才会进行熔断判断
统计时长:在多少ms之内进行统计
当请求参数如下时不会发生熔断
当请求参数如下时,会发生熔断
9. Sentinel+Feign熔断后降级处理
当熔断之后,进行降级处理;在FeignClient注解上加上fallback,就是说其降级处理是BusinessFeignFallback.class
@FeignClient(value = "business", fallback = BusinessFeignFallback.class)
public interface BusinessFeign {
@GetMapping("/business/admin/daily-train/gen-daily/{date}")
CommonResp<Object> genDaily(@PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") Date date);
@GetMapping("/business/hello")
String hello();
}
@Component
public class BusinessFeignFallback implements BusinessFeign {
@Override
public String hello() {
return "Fallback";
}
@Override
public CommonResp<Object> genDaily(Date date) {
return null;
}
}
远程调用的hello方法定义
@SentinelResource("hello")
@GetMapping("/hello")
public String hello()throws InterruptedException{
return "Hello World! Business!";
}
10. 熔断策略
异常比例策略:
比例阈值:0-1之间,
最小请求数:当每秒钟的请求数大于6时
当出现异常的请求数大于比例阈值时会进行熔断,熔断时间为3秒
异常数:
区别在于异常数和比例阈值,在异常数策略中,只要异常请求达到3个便会触发熔断