钻研一下 sentinel 实现的外围算法
<!-- replace here with the latest version -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.3</version>
</dependency>
官网应用阐明
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
// set limit qps to 20
rule.setCount(20);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rules.add(rule);
FlowRuleManager.loadRules(rules);
try (Entry entry = SphU.entry("HelloWorld")) {
// Your business logic here.
System.out.println("hello world");
} catch (BlockException e) {
// Handle rejected request.
e.printStackTrace();}
// try-with-resources auto exit
为了看一下外围实现环节,为了更直观的进行了解,这里把一秒分成两个窗口,即每个窗口 slidingWindowSize 为 500, 并且进行了简洁的从新编写
private static long slidingWindowSize = 500L;
private AtomicReferenceArray<Item> array = new AtomicReferenceArray<Item>(2);
private ReentrantLock lock = new ReentrantLock();
private long permitsPerSecond; // 设定每秒容许的 qps
入口办法
public boolean entry() {
try {long currentTimeMillis = System.currentTimeMillis();
int index = findSlidingWindowIndex(currentTimeMillis);
long slidingWindowStart = findSlidingWindowStart(currentTimeMillis);
Item item = currentItem(index, slidingWindowStart);
if (item == null) {return true;}
if (accumulatedPassQps(1 - index, item) > permitsPerSecond) {return false;}
item.passIncrement();
return true;
} catch (Exception ex) {return true;}
}
累计曾经通过的 qps
private long accumulatedPassQps(int index, Item item) {Item other = array.get(index);
if (other == null
|| (Math.abs(other.getSlidingWindowStart() - item.getSlidingWindowStart()) > slidingWindowSize)) {return item.passValue();
}
return other.passValue() + item.passValue();
}
以后是哪个工夫窗口
private Item currentItem(int index, long slidingWindowStart) {while (true) {Item previous = array.get(index);
if (previous == null) {Item item = new Item(slidingWindowStart);
if (array.compareAndSet(index, null, item)) {return item;} else {Thread.yield();
}
} else if (slidingWindowStart == previous.getSlidingWindowStart()) {return previous;} else if (slidingWindowStart > previous.getSlidingWindowStart()) {if (lock.tryLock()) {
try {return previous.reset(slidingWindowStart);
} finally {lock.unlock();
}
} else {Thread.yield();
}
} else {return null;}
}
}
private long findSlidingWindowStart(long timeMillis) {return timeMillis - timeMillis % slidingWindowSize;}
private int findSlidingWindowIndex(long timeMillis) {return (int) ((timeMillis / slidingWindowSize) % 2);
}
Item 封装的内容
private long slidingWindowStart;
private LongAdder accu;
public Item reset(long slidingWindowStart) {
this.slidingWindowStart = slidingWindowStart;
accu.reset();
return this;
}
public long passValue() {return accu.sum();
}
public void passIncrement() {accu.increment();
}
public long getSlidingWindowStart() {return slidingWindowStart;}