为什么sentinel的流量控制不准,而且处理模式为 均速排队时不生效?
业务代码:
@Service
public class SentinelServiceImpl implements SentinelService {
private final AtomicInteger NUM = new AtomicInteger(0);
@PostConstruct
private void initFlowRules(){
System.out.println("init sentinel rules");
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("name");
//0线程,1QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//处理模式
//直接拒绝:(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的
//Warm Up:(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动
//均速排队:(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
// 阈值
rule.setCount(2);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
@SentinelResource(value = "name",blockHandler = "reject")
@Override
public String getMsg() {
int andIncrement = NUM.getAndIncrement();
System.out.println(new Date() + ":get msg success :" + andIncrement);
return "success" + andIncrement;
}
public String reject(BlockException ex){
int andIncrement = NUM.getAndIncrement();
System.out.println(new Date() + ":blockHandler:" + andIncrement);
return "reject" + andIncrement;
}
}
设置QPS为2,模式为 匀速排队,看看调用方的同时发生10个请求的结果:
1682674982090--> result:success1
1682674982090--> result:success0
1682674982090--> result:reject3
1682674982090--> result:success2
1682674982093--> result:reject4
1682674982095--> result:reject5
1682674982095--> result:reject7
1682674982096--> result:reject6
1682674982098--> result:reject8
1682674982552--> result:success9
首先一秒了通过了4个请求,其次超流量的请求没有排队等待处理,而是直接走了拒绝了
public class SimpleEntryMain {
private static String resourceName = "simpleEntry";
public static void main(String[] args) throws InterruptedException {
initFlowRule();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new RunTask());
t.setName("simulate-traffic-Task");
t.start();
Thread.sleep(50);
}
}
private static void initFlowRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resourceName);
rule1.setCount(2);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
static class RunTask implements Runnable {
@Override
public void run() {
Entry entry = null;
try {
entry = SphU.entry(resourceName);
doSomeThing();
} catch (BlockException e1) {
System.out.println("Block ----------");
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
public static void doSomeThing(){
System.out.println("++++++++++++++++++");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// ignore
}
}
}
这个demo首先是通过initFlowRule()
方法设置了规则,然后我们通过多线程来模拟多个调用规则,方法的调用我们简单的模拟花费Thread.sleep(3000)
也就是3秒。然后线程中运行的就是Sentinel
的使用:
Entry entry = null;
try {
entry = SphU.entry(resourceName);
doSomeThing();
} catch (BlockException e1) {
System.out.println("Block ----------");
} finally {
if (entry != null) {
entry.exit();
}
}
这里就是try-catch-finally
的标准使用流程,其主要是通过SphU.entry(resourceName)
的调用在里面解析规则的配置校验,如果能通过的话就不会抛出异常,如果不能通过就抛出BlockException
异常。我们上面是直接使用,当我们在SpringBoot
中直接使用的话,其本身的封装可能使用动态代理或者AOP的方式来将这里的doSomeThing
与SphU.entry(resourceName)
的获取做解耦,我们加了注解后就可以直接专注于我们的doSomeThing()
的方法逻辑。