当前位置:网站首页>Sentinel sliding window traffic statistics

Sentinel sliding window traffic statistics

2022-07-06 04:19:00 Keep walking

StatisticSlot There are mainly two types of data :

  • Number of threads
  • Number of requests , That is to say QPS

It is relatively simple to count the number of threads , Through internal maintenance LongAdder Statistics of the current number of threads , Add 1, Thread execution completion 1, To get the number of threads .
about QPS The statistics of are more complicated , The principle of sliding window is used . Now let's focus on the details of the implementation .

Bucket

Sentinel Use Bucket Count the index data within a window , These metrics include the total number of requests 、 The total number of successes 、 Total exceptions 、 Total time 、 Minimum time 、 Maximum time consumption, etc , And one Bucket It can be a record 1s The data in , It can also be 10ms The data in , This length of time is called window time .

public class MetricBucket {
    /**
     *  Store the count of each event , For example, the total number of exceptions 、 Total requests, etc 
     */
    private final LongAdder[] counters;
    /**
     *  The minimum time spent in this event 
     */
    private volatile long minRt;
}

Bucket The index data recorded over a period of time is a LongAdder Array ,LongAdder Ensure the atomicity of data modification , And the performance is better AtomicInteger Perform better . Each element of the array records the total number of requests for a time window 、 Number of abnormal 、 Time consuming .
Sentinel Using enumerated types MetricEvent Of ordinal Attribute as subscript , When you need to get Bucket Record the total number of successful requests or exceptions 、 Total request processing time , Depending on the type of event (MetricEvent) from Bucket Of LongAdder Get the corresponding... From the array LongAdder, And call sum Method to get :

//  Suppose the event is  MetricEvent.SUCCESS
public long get(MetricEvent event) {
    // MetricEvent.SUCCESS.ordinal() by  1
    return counters[event.ordinal()].sum();
}

When the number of requests needs to be recorded, the operation is as follows :

//  Suppose the event is  MetricEvent.RT
public void add(MetricEvent event, long n) {
     // MetricEvent.RT.ordinal() by  2
     counters[event.ordinal()].add(n);
}

The sliding window

We want to know the number of successful requests processed by an interface per second ( success QPS)、 The average time taken for a request is (avg rt), We just need to control Bucket Just count the index data of one second of plutonium .Sentinel How to achieve it ? It defines a Bucket Array , Locate the subscript of the array according to the timestamp . Suppose we need to count every 1 Number of requests processed in seconds , And only need to save the data of the last minute , that Bucket The size of the array can be set to 60, Every Bucket Of windowLengthInMs( Window time ) Size is 1000ms.
We can't and certainly don't need unlimited storage Bucket, If you only need to keep the data for one minute , Then we can put Bucket The size of is set to 60 And recycle , Avoid frequent creation Bucket. How to locate in this case Bucket Well ? The method is to remove the millisecond part of the current timestamp and wait for the current second , Then take the remainder of the obtained seconds and the length of the array , You can get the current time window Bucket Position in the array .
For example, given a timestamp, calculate the array index :

private int calculateTimeIdx(long timeMillis) {
        /**
         *  Assume that the current timestamp is  1577017699235
         * windowLengthInMs  by  1000  millisecond (1  second )
         *  be 
         *  Turn milliseconds into seconds  => 1577017699
         *  Then the length of the array is remainder => Index mapped to array 
         *  Remainder is to recycle arrays 
         */
        long timeId = timeMillis / windowLengthInMs;
        return (int) (timeId % array.length());
    }

Because the array is recycled , The current timestamp and the timestamp before one minute and the timestamp after one minute will be mapped to the same one in the array Bucket, therefore , You must be able to judge what you have achieved Bucket Whether to count the indicator data in the current time window , This requires that each element of the array be stored Bucet The start timestamp of the time window . What about the starting time ?

    protected long calculateWindowStart(long timeMillis) {
        /**
         *  Suppose the window size is  1000  millisecond , That is, each element of the array stores  1  Second statistics 
         * timeMillis % windowLengthInMs  Is to get the millisecond part 
         * timeMillis -  Number of milliseconds  =  Second part 
         *  This gives the start timestamp per second 
         */
        return timeMillis - timeMillis % windowLengthInMs;
    }

WindowWrap

because Bucket Time window information is not saved by itself , therefore Sentinel to Bucket Add a packaging class WindowWrap, Used to record Bcuket Time window of .

public class WindowWrap<T> {
    /**
     *  Window length ( millisecond )
     */
    private final long windowLengthInMs;
    /**
     *  Start timestamp ( millisecond )
     */
    private long windowStart;
    /**
     *  The content of the time window , stay  WindowWrap  This value is represented by generics in ,
     *  But it's actually  MetricBucket  class 
     */
    private T value;
    public WindowWrap(long windowLengthInMs, long windowStart, T value) {
        this.windowLengthInMs = windowLengthInMs;
        this.windowStart = windowStart;
        this.value = value;
    }
}

We just need to know the start time and the size of the window , Give a timestamp , You can know whether the timestamp is Bucket Window time .

/**
     *  Check whether the given timestamp is currently  bucket  in .
     *
     * @param timeMillis  Time stamp , millisecond 
     * @return
     */
    public boolean isTimeInWindow(long timeMillis) {
        return windowStart <= timeMillis && timeMillis < windowStart + windowLengthInMs;
    }

Locate by timestamp Bucket

Bucket Used for statistics of various index data ,WindowWrap Used to record Bucket Time window information , Record the start time and size of the window ,WindowWrap An array is a sliding window .
When a request is received , An array index can be calculated according to the requested timestamp , From the sliding window (WindowWrap) Get one of WindowWrap, To obtain WindowWrap Packaged Bucket, call Bucket Of add Method to record the corresponding event .

/**
     *  Get from timestamp  bucket
     *
     * @param timeMillis  Time stamp ( millisecond )
     * @return  If time is valid , Then the current bucket item is displayed at the provided timestamp ; If the time is invalid , Is empty 
     */
    public WindowWrap<T> currentWindow(long timeMillis) {
        if (timeMillis < 0) {
            return null;
        }
        //  Get the array index to which the timestamp is mapped 
        int idx = calculateTimeIdx(timeMillis);
        //  Calculation  bucket  The start time of the time window 
        long windowStart = calculateWindowStart(timeMillis);

        //  Get... From the array  bucket
        while (true) {
            WindowWrap<T> old = array.get(idx);
            //  Usually when the project starts , Time has not reached a cycle , The array is not full , There is no reuse stage , So the array element may be empty 
            if (old == null) {
                //  Create a new  bucket, And create a  bucket  Wrappers 
                WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
                // cas  write in , Make sure threads are safe , Expect the element of the array subscript to be empty , Otherwise, do not write , It's reuse 
                if (array.compareAndSet(idx, null, window)) {
                    return window;
                } else {
                    Thread.yield();
                }
            }
            //  If  WindowWrap  Of  windowStart  It is exactly the start time of the time window calculated by the current timestamp , Is what we want  bucket
            else if (windowStart == old.windowStart()) {
                return old;
            }
            //  Reuse the old  bucket
            else if (windowStart > old.windowStart()) {
                if (updateLock.tryLock()) {
                    try {
                        //  Reset  bucket, And designate  bucket  The start time of the new time window 
                        return resetWindowTo(old, windowStart);
                    } finally {
                        updateLock.unlock();
                    }
                } else {
                    Thread.yield();
                }
            }
            //  Calculated current  bucket  The start time of the time window is greater than that currently stored in the array  bucket  The start time of the time window is still small ,
            //  Go straight back to an empty  bucket  Just go 
            else if (windowStart < old.windowStart()) {
                return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
            }
        }
    }

The above code calculates the current time window through the current timestamp Bucket(new Bucket) Index in array , To stay Bucket The start time of the time window , Get from the array by index Bucket(old bucket).

  • When the index does not exist Bucket when , Create a new Bucket, And thread safe write to the index , And then Bucket return
  • When old Bucket Isn't empty , And old Bucket The start time of the time window is the same as the current calculated new Bucket The start time of the time window is equal , Then Bucket Is what we are looking for Bucket, Go straight back to
  • When calculating new Bucket The start time of the time window is greater than that stored in the current array old Bucket The start time of the time window , You can reuse this old Bucket, Reset with thread safety
  • When we calculate new Bucket The start time of the time window is less than that stored in the current array old Bucket The start time of the time window , Go straight back to an empty Bucket.

How to get the previous one of the current timestamp Bucket Well , The answer is to calculate the current... Based on the current timestamp Bucket Time window start time , Use current Bucket The start time of the time window minus the size of a window can locate the previous Bucket 了 .
Need to pay attention to when , Arrays are recycled , So at the moment Bucket Compared with the calculated Bucket It may differ by one sliding window or more than one , So it needs to be based on Bucket The start time of the time window of is compared with the current timestamp , If you cross a cycle, it is invalid .

summary

  • WindowWrap For packing Bucket, With Bucket Together to create
  • WindowWrap Array implementation sliding window ,Bucket Only responsible for statistics of various index data ,WindowWrap Used to record Bucket Time window information
  • location Bucket It's actually positioning WindowWrap, Get WindowWrap You can get Bucket
原网站

版权声明
本文为[Keep walking]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202132233563646.html