sentinel令牌桶限流算法

研究一下sentinel实现的核心算法



    com.alibaba.csp
    sentinel-core
    1.8.3

官方使用说明

List 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 array = new AtomicReferenceArray(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;
    }

你可能感兴趣的:(java)