上篇文章已经讲解了 sentinel 的安装以及简单使用,今天来说说 sentinel-dashbord 的所有功能。
一、限流
单机限流操作
单机限流是比较简单的,我们直接看图是如何操作的
以上测试为限制 single 接口的 qps 为 2,实际测试数量为 4,通过 3 个请求,拒绝 1 个请求。
单机限线程数操作
并发线程数限流用于保护业务线程数不被耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发线程数限流不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目,如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。
public static <T> T flow(String resourceName, Supplier supplier){
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
T result = null;
Entry entry = null;
try {
entry = SphU.entry(resourceName);
// 被保护的业务逻辑
result = (T)supplier.get();
return result;
} catch (BlockException ex) {
ex.fillInStackTrace().getStackTrace();
System.out.println(ex.fillInStackTrace());
// 资源访问阻止,被限流或被降级
// 在此处进行相应的处理操作
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
}finally {
// 务必保证 exit,务必保证每个 entry 与 exit 配对
if (entry != null) {
entry.exit();
}
}
return result;
}
public static void main(String[] args) throws InterruptedException {
String resourceName = "test";
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule(resourceName);
rule.setCount(1);
rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(100);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,3,5,TimeUnit.SECONDS,arrayBlockingQueue
);
for(int i=0;i<6;i++){
int index = i;
CompletableFuture.runAsync(() -> {
flow(resourceName,() -> {
int num = (int)(Math.random()*11);
try {
TimeUnit.SECONDS.sleep(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
String result = "idnex: "+index + " time: " +num+ "s name: " + Thread.currentThread().getName()+ " queueSize: "+arrayBlockingQueue.size();
System.out.println(result);
return result;
});
},threadPoolExecutor);
}
TimeUnit.SECONDS.sleep(10);
threadPoolExecutor.shutdown();
}
运行结果
idnex: 1 time: 2s name: pool-1-thread-2 queueSize: 3
com.alibaba.csp.sentinel.slots.block.flow.FlowException
com.alibaba.csp.sentinel.slots.block.flow.FlowException
com.alibaba.csp.sentinel.slots.block.flow.FlowException
idnex: 0 time: 4s name: pool-1-thread-1 queueSize: 0
idnex: 2 time: 8s name: pool-1-thread-3 queueSize: 0
结果表示,我只允许运行3个线程,但请求(并发)数量实际大于3个线程,且我定的3个线程都在执行任务,所以其余请求被异常处理。
留一个疑问点,关于 FlowRule 有一个count的设置,如果说 Grade 为 THREAD 模式,count 到底是一个开关 还是一个数字,如果是数字,测试count 值 0<count<3的时候有线程拒绝,3<=count<=无穷大 无线程拒绝,count 在 Grade 为 THREAD 模式是什么意思,限制的是什么?
集群限流操作
集群线程数操作
二、降级
RT降级操作
@SentinelResource(fallback = "hystrixException")
@GetMapping("/hystrix")
public String hystrix(){
int num = (int) (Math.random() * 10);
try {
TimeUnit.SECONDS.sleep(num);
}catch(Exception e){
}
System.out.println("熔断请求 -> 处理时间: "+num+"s");
return "熔断请求";
}
上图可以看出,我设置 hystrix 接口要被请求 20次,当响应时间在 规定时间 内的平均值大于设置的阈值(RT:2000ms),则拒绝所有请求。时间窗口 的意思是,若发生熔断则特定时间内的所有请求均被拒绝,直到时间窗口数值耗尽才开始接收请求。
这里有个问题 规定时间 如何限制?? 查到官方说法如下,可我测试一定是大于1s了。
异常比列
@SentinelResource(fallback = "hystrixException")
@GetMapping("/hystrixExcep")
public String hystrixExcep(){
int num = (int) (Math.random() * 10);
System.out.println("熔断请求 -> num: "+num);
if(num >= 5){
throw new RuntimeException("num: "+num);
}
return "熔断请求";
}
public String hystrixException(Throwable throwable){
System.out.println("熔断异常 -> "+throwable.getMessage());
return "熔断异常";
}
# 运行结果
熔断请求 -> num: 1
熔断请求 -> num: 7
熔断异常 -> num: 7
熔断请求 -> num: 2
熔断请求 -> num: 0
熔断请求 -> num: 9
熔断异常 -> num: 9
熔断异常 -> null
熔断异常 -> null
熔断异常 -> null
熔断异常 -> null
熔断异常 -> null
假设1秒有10个请求,我的异常比是30%,那就是只要有3个请求发生异常,其他请求均被拒绝,拒绝的请求还是有走定义的熔断异常方法。
异常数
异常数是很好理解的,和异常比是相似的。假设1分钟内有100个请求,100个请求中有部分请求发生异常,且1分钟内异常数刚好>=设置的阈值,则其余请求均被拒绝,拒绝的请求还是有走定义的熔断异常方法。
三、热点
热点数据限流就是对访问频次比较高的数据进行限制。在 dashbord 中参数索引的意思就是,方法中的参数列表的下标,默认是0开始
在阅读文档的时候发现,文档中之说了 统计一段时间内的 频次,但并没有明确说明是 1s 还是 1minute。
四、授权
作者最近没时间多去研究,等实际有用到我一定会回来补。