एन सेकंड में थ्रॉटलिंग विधि एम अनुरोधों पर कॉल करती है


137

मुझे एक घटक / वर्ग की आवश्यकता है जो एन सेकंड (या एमएस या नैनो में अधिकतम एम कॉल पर कुछ विधि का निष्पादन थ्रॉटल करता है, इससे कोई फर्क नहीं पड़ता)।

दूसरे शब्दों में, मुझे यह सुनिश्चित करने की ज़रूरत है कि मेरी विधि N सेकंड की स्लाइडिंग विंडो में M बार से अधिक नहीं निष्पादित हो।

यदि आप नहीं जानते कि मौजूदा वर्ग आपके समाधान / विचारों को पोस्ट करने के लिए स्वतंत्र है तो आप इसे कैसे लागू करेंगे।



3
इस समस्या के कुछ बेहतरीन जवाब हैं stackoverflow.com/questions/667508/…
skaffman

> मुझे यह सुनिश्चित करने की आवश्यकता है कि मेरी विधि है> N सेकंड के एक> स्लाइडिंग विंडो में M से अधिक बार निष्पादित नहीं किया गया है। मैंने हाल ही में एक ब्लॉग पोस्ट लिखा है कि यह कैसे करना है। .NET में। आप जावा में कुछ समान बनाने में सक्षम हो सकते हैं। .NET में बेहतर दर सीमा
जैक लीच

मूल प्रश्न इस ब्लॉग पोस्ट में हल की गई समस्या की तरह लगता है: [जावा मल्टी-चैनल एसिंक्रोनस थ्रॉटलर ] ( cordinc.com/blog/2010/04/java-multichannel-asynchronous.html )। N सेकंड में M कॉल की दर के लिए, इस ब्लॉग में चर्चा की गई थ्रॉटलर गारंटी देता है कि समयरेखा पर लंबाई N के किसी भी अंतराल में M कॉल से अधिक नहीं होगी।
एचबीएफ

जवाबों:


81

मैं समय के निश्चित आकार के साथ टाइमस्टैम्प के रिंग बफर का उपयोग करता हूं । प्रत्येक बार विधि को कॉल करने पर, आप सबसे पुरानी प्रविष्टि की जांच करते हैं, और यदि यह अतीत में एन सेकंड से कम है, तो आप निष्पादित करते हैं और एक और प्रविष्टि जोड़ते हैं, अन्यथा आप सोते हैं समय के अंतर के लिए।


4
लवली। बस मैं यह चाहता हूं। त्वरित प्रयास इसे और न्यूनतम स्मृति पदचिह्न लागू करने के लिए ~ 10 लाइनें दिखाते हैं। बस थ्रेड सुरक्षा और आने वाले अनुरोधों की कतार के बारे में सोचने की जरूरत है।
vtrubnikov

5
इसीलिए आप jay.util.concurrent से DelayQueue का उपयोग करें। यह एक ही प्रविष्टि पर अभिनय करने वाले कई थ्रेड्स की समस्या को रोकता है।
इरिकसन

5
एक बहु थ्रेडेड केस के लिए, टोकन बकेट दृष्टिकोण एक बेहतर विकल्प हो सकता है, मुझे लगता है।
माइकल बोर्गवर्ड

1
क्या आप जानते हैं कि इस एल्गोरिथम को कैसे कहा जाता है अगर इसका कोई नाम है?
व्लादो पांडेसिक

80

मेरे लिए बॉक्स से बाहर काम करना Google Guava RateLimiter था

// Allow one request per second
private RateLimiter throttle = RateLimiter.create(1.0);

private void someMethod() {
    throttle.acquire();
    // Do something
}

19
मैं इस समाधान की सिफारिश नहीं करूंगा क्योंकि अमरूद रेटलाइटर धागे को अवरुद्ध कर देगा और इससे धागा पूल आसानी से समाप्त हो जाएगा।
काव्यादिस

18
@kaviddiss अगर आप ब्लॉक करने के लिए नहीं करना चाहते हैं तो का उपयोगtryAquire()
SLF

7
वर्तमान में रेटलीमिटर के कार्यान्वयन (कम से कम मेरे लिए) के साथ मुद्दा यह है कि यह 1 सेकंड से अधिक की समय अवधि के लिए अनुमति नहीं देता है और इसलिए उदाहरण के लिए 1 प्रति मिनट की दर।
जॉन बी

4
@ जॉन बी जहां तक ​​मैं समझता हूं, आप RateLimiter.create (60.0) + rateLimiter.acquire (60)
divideByZero

2
@radiantRazor Ratelimiter.create (1.0 / 60) और अधिग्रहित () प्रति मिनट 1 कॉल प्राप्त करता है।
बिजेंटस

30

ठोस शब्दों में, आपको इसे लागू करने में सक्षम होना चाहिए DelayQueue। प्रारंभिक रूप M Delayedसे शून्य पर सेट देरी के साथ उदाहरणों के साथ कतार को प्रारंभ करें । विधि के अनुरोध के रूप में, takeएक टोकन आता है, जो इस विधि को अवरुद्ध करने का कारण बनता है जब तक कि थ्रॉटलिंग की आवश्यकता पूरी नहीं हुई है। जब एक टोकन लिया गया है, तो addदेरी के साथ कतार में एक नया टोकन N


1
हाँ, यह चाल होगी। लेकिन मैं विशेष रूप से DelayQueue को पसंद नहीं करता हूं क्योंकि यह एक संतुलित बाइनरी हैश (जिसका अर्थ है कि बहुत सारी तुलना offerऔर संभावित सरणी वृद्धि) का उपयोग कर रहा है ( और इसका अर्थ है कि मेरे लिए सभी थोड़े भारी हैं)। मुझे लगता है कि दूसरों के लिए यह पूरी तरह से ठीक हो सकता है।
vtrubnikov

5
दरअसल, इस एप्लिकेशन में, चूंकि नए तत्व को ढेर में जोड़ा जाता है, इसलिए लगभग हमेशा हीप में अधिकतम तत्व होगा (यानी, सबसे लंबे समय तक विलंब होता है), आमतौर पर प्रति ऐड एक तुलना की आवश्यकता होती है। इसके अलावा, अगर एल्गोरिथ्म को सही ढंग से लागू किया गया है, तो सरणी कभी नहीं बढ़ेगी, क्योंकि एक तत्व को एक तत्व लेने के बाद ही जोड़ा जाता है।
एरिकसन

3
मुझे यह उन मामलों में भी मददगार लगा, जहाँ आप नहीं चाहते कि बड़े आकार के एम रखने से और बड़े पैमाने पर फटने के लिए अनुरोध हो और मुट्ठी भर मिल के क्रम में N अपेक्षाकृत छोटा हो। जैसे। M = 5, N = 20ms 5 के आकार में होने के लिए 250 / सेकंड की केपिंग फट के माध्यम से प्रदान करेगा
FUD

क्या यह पैमाने एक मिलियन आरपीएम के लिए और जब समवर्ती अनुरोधों की अनुमति होगी? मुझे एक लाख विलंब से जोड़ने की आवश्यकता होगी। इसके अलावा कोने के मामले विलंबता पर अधिक होंगे - ऐसा मामला जहां कई थ्रेड पोल () कह रहे हों और यह हर बार लॉक हो रहा हो।
आदित्य जोशी

@ AdityaJoshee मैंने इसे बेंचमार्क नहीं किया है, लेकिन अगर मुझे कुछ समय मिलता है तो मैं ओवरहेड की भावना लाने की कोशिश करूंगा। एक बात पर ध्यान दें कि आपको 1 सेकंड में समाप्त होने वाले 1 मिलियन टोकन की आवश्यकता नहीं है। आपके पास 100 टोकन हो सकते हैं, जो 10 मिलीसेकंड में समाप्त होते हैं, 10 टोकन जो कि मिलीसेकंड में समाप्त होते हैं, आदि। यह वास्तव में तात्कालिक दर को औसत दर के करीब होने के लिए मजबूर करता है, स्पाइक्स को चौरसाई करता है, जो क्लाइंट पर बैकअप का कारण बन सकता है, लेकिन यह एक स्वाभाविक परिणाम है। सीमित करने की दर। 1 मिलियन आरपीएम शायद ही थ्रॉटलिंग जैसा लगता है। यदि आप अपने उपयोग के मामले की व्याख्या कर सकते हैं तो मेरे पास बेहतर विचार हो सकते हैं।
एरिक्सन

21

टोकन बाल्टी एल्गोरिथ्म पर पढ़ें । मूल रूप से, आपके पास इसमें टोकन के साथ एक बाल्टी है। हर बार जब आप विधि निष्पादित करते हैं, तो आप एक टोकन लेते हैं। यदि अधिक टोकन नहीं हैं, तो आप तब तक ब्लॉक करते हैं जब तक कि आप एक प्राप्त न कर लें। इस बीच, कुछ बाहरी अभिनेता हैं जो एक निश्चित अंतराल पर टोकन की भरपाई करते हैं।

मुझे ऐसा करने के लिए लाइब्रेरी (या कुछ भी समान) के बारे में पता नहीं है। आप इस तर्क को अपने कोड में लिख सकते हैं या व्यवहार को जोड़ने के लिए AspectJ का उपयोग कर सकते हैं।


3
सुझाव के लिए धन्यवाद, दिलचस्प अहंकार। लेकिन इसकी वैसी नहीं है जैसी मुझे चाहिए। उदाहरण के लिए, मुझे निष्पादन को 5 कॉल प्रति सेकंड तक सीमित करने की आवश्यकता है। यदि मैं टोकन बाल्टी का उपयोग करता हूं और एक ही समय में 10 अनुरोध आते हैं, तो पहले 5 कॉल सभी उपलब्ध टोकन ले लेंगे और क्षण भर में निष्पादित करेंगे, जबकि शेष 5 कॉल 1/5 s के निश्चित अंतराल पर निष्पादित किए जाएंगे। ऐसी स्थिति में मुझे शेष 5 कॉल की जरूरत है, जिसे केवल 1 सेकंड के पास होने के बाद सिंगल फट में निष्पादित किया जाए।
vtrubnikov

5
क्या होगा यदि आप बाल्टी में हर सेकंड में 5 (या 5 - (5-शेष) को 1 1/5 सेकंड के बजाय जोड़ते हैं?
केविन

@ केविन नहीं यह अभी भी मुझे 'स्लाइडिंग विंडो' प्रभाव नहीं देगा
vtrubnikov

2
@valery हाँ यह होगा। (हालांकि एम पर टोकन कैप करने के लिए याद रखें)
nos

"बाहरी अभिनेता" के लिए कोई ज़रूरत नहीं है। यदि आप मेटाडाटा को अनुरोध समय के आसपास रखते हैं, तो सब कुछ एकल पिरोया जा सकता है।
मार्सेलस वालेस

8

यदि आपको एक जावा आधारित स्लाइडिंग विंडो दर सीमक की आवश्यकता है जो एक वितरित प्रणाली में संचालित होगा जिसे आप https://github.com/mokies/ratelimitj परियोजना पर एक नज़र रखना चाहते हैं ।

IP द्वारा 50 प्रति मिनट तक अनुरोधों को सीमित करने के लिए एक रेडिस समर्थित कॉन्फ़िगरेशन इस तरह दिखेगा:

import com.lambdaworks.redis.RedisClient;
import es.moki.ratelimitj.core.LimitRule;

RedisClient client = RedisClient.create("redis://localhost");
Set<LimitRule> rules = Collections.singleton(LimitRule.of(1, TimeUnit.MINUTES, 50)); // 50 request per minute, per key
RedisRateLimit requestRateLimiter = new RedisRateLimit(client, rules);

boolean overLimit = requestRateLimiter.overLimit("ip:127.0.0.2");

देखें https://github.com/mokies/ratelimitj/tree/master/ratelimitj-redis Redis विन्यास पर अधिक जानकारी सामने।


5

यह एप्लिकेशन में निर्भर करता है।

उस मामले की कल्पना करें जिसमें कई धागे चाहते हैं कि कोई भी वैश्विक स्तर पर सीमित-सीमित कार्रवाई के साथ कोई फट की अनुमति न हो (यानी आप प्रति सेकंड 10 क्रियाओं को सीमित करना चाहते हैं, लेकिन आप नहीं चाहते कि 10 क्रियाएं पहले दूसरे में हों और फिर रहें 9 सेकंड रुक गए)।

DelayedQueue का एक नुकसान है: जिस क्रम पर धागे अनुरोध करते हैं, टोकन वह आदेश नहीं हो सकता है जिस पर वे अपना अनुरोध पूरा करते हैं। यदि कई थ्रेड्स टोकन के इंतजार में अवरुद्ध हैं, तो यह स्पष्ट नहीं है कि कौन सा अगले उपलब्ध टोकन लेगा। तुम भी हमेशा के लिए मेरी बात में इंतज़ार कर धागे हो सकता है।

एक समाधान के लिए दो लगातार क्रियाओं के बीच समय का न्यूनतम अंतराल होना चाहिए , और उसी क्रम में कार्रवाई करना चाहिए जैसा कि उनसे अनुरोध किया गया था।

यहाँ एक कार्यान्वयन है:

public class LeakyBucket {
    protected float maxRate;
    protected long minTime;
    //holds time of last action (past or future!)
    protected long lastSchedAction = System.currentTimeMillis();

    public LeakyBucket(float maxRate) throws Exception {
        if(maxRate <= 0.0f) {
            throw new Exception("Invalid rate");
        }
        this.maxRate = maxRate;
        this.minTime = (long)(1000.0f / maxRate);
    }

    public void consume() throws InterruptedException {
        long curTime = System.currentTimeMillis();
        long timeLeft;

        //calculate when can we do the action
        synchronized(this) {
            timeLeft = lastSchedAction + minTime - curTime;
            if(timeLeft > 0) {
                lastSchedAction += minTime;
            }
            else {
                lastSchedAction = curTime;
            }
        }

        //If needed, wait for our time
        if(timeLeft <= 0) {
            return;
        }
        else {
            Thread.sleep(timeLeft);
        }
    }
}

minTimeयहाँ क्या मतलब है? यह क्या करता है? क्या आप उस पर व्याख्या कर सकते हैं?
फ्लैश

minTimeसमय की न्यूनतम राशि है जो अगले टोकन का उपभोग करने से पहले एक टोकन का उपभोग करने के बाद पारित करने की आवश्यकता होती है।
Duarte Meneses

3

यद्यपि यह वह नहीं है जो आपने पूछा था, ThreadPoolExecutorजो एम सेकंड के बजाय एन सेकंड में एम अनुरोधों के लिए कैप करने के लिए डिज़ाइन किया गया है, यह भी उपयोगी हो सकता है।


2

मैं algorithm.Try थ्रॉटलिंग एक सरल इस लिंक, को लागू किया है http://krishnaprasadas.blogspot.in/2012/05/throttling-algorithm.html

एल्गोरिथ्म के बारे में एक संक्षिप्त जानकारी,

यह एल्गोरिथ्म जावा विलंबित कतार की क्षमता का उपयोग करता है । अपेक्षित देरी के साथ एक विलंबित वस्तु बनाएं (यहां मिलिसकंड टाइम यूनीट के लिए 1000 / एम )। विलम्बित कतार में वही वस्तु डालें जो हमारे लिए चलती हुई खिड़की प्रदान करे। फिर प्रत्येक विधि कॉल करने से पहले ले वस्तु कतार फार्म, ले एक अवरुद्ध कॉल जो केवल निर्दिष्ट विलंब के बाद वापस आ जाएगी है, और विधि कॉल के बाद मत भूलना अद्यतन समय के साथ कतार में वस्तु डाल करने के लिए (यहाँ वर्तमान मिलीसेकंड) ।

यहाँ हम विभिन्न देरी के साथ कई विलंबित वस्तुएं भी रख सकते हैं। यह दृष्टिकोण उच्च थ्रूपुट भी प्रदान करेगा।


6
आपको अपने एल्गोरिथ्म का सारांश पोस्ट करना चाहिए। अगर आपका लिंक चला गया तो आपका जवाब बेकार हो जाता है।
jwr

धन्यवाद, मैंने संक्षेप में जोड़ा है।
कृष्ण

1

नीचे मेरा कार्यान्वयन मनमाने ढंग से अनुरोध समय की सटीकता को संभाल सकता है, इसमें प्रत्येक अनुरोध के लिए O (1) समय जटिलता है, इसके लिए किसी अतिरिक्त बफर, जैसे O (1) स्थान की जटिलता की आवश्यकता नहीं है, इसके अलावा इसके बजाय टोकन जारी करने के लिए पृष्ठभूमि थ्रेड की आवश्यकता नहीं है अंतिम अनुरोध के बाद पारित किए गए समय के अनुसार टोकन जारी किए जाते हैं।

class RateLimiter {
    int limit;
    double available;
    long interval;

    long lastTimeStamp;

    RateLimiter(int limit, long interval) {
        this.limit = limit;
        this.interval = interval;

        available = 0;
        lastTimeStamp = System.currentTimeMillis();
    }

    synchronized boolean canAdd() {
        long now = System.currentTimeMillis();
        // more token are released since last request
        available += (now-lastTimeStamp)*1.0/interval*limit; 
        if (available>limit)
            available = limit;

        if (available<1)
            return false;
        else {
            available--;
            lastTimeStamp = now;
            return true;
        }
    }
}

0

इस सरल दृष्टिकोण का उपयोग करने का प्रयास करें:

public class SimpleThrottler {

private static final int T = 1; // min
private static final int N = 345;

private Lock lock = new ReentrantLock();
private Condition newFrame = lock.newCondition();
private volatile boolean currentFrame = true;

public SimpleThrottler() {
    handleForGate();
}

/**
 * Payload
 */
private void job() {
    try {
        Thread.sleep(Math.abs(ThreadLocalRandom.current().nextLong(12, 98)));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.err.print(" J. ");
}

public void doJob() throws InterruptedException {
    lock.lock();
    try {

        while (true) {

            int count = 0;

            while (count < N && currentFrame) {
                job();
                count++;
            }

            newFrame.await();
            currentFrame = true;
        }

    } finally {
        lock.unlock();
    }
}

public void handleForGate() {
    Thread handler = new Thread(() -> {
        while (true) {
            try {
                Thread.sleep(1 * 900);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                currentFrame = false;

                lock.lock();
                try {
                    newFrame.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    });
    handler.start();
}

}



0

यह ऊपर LeakyBucket कोड का अपडेट है। यह प्रति सेकंड 1000 अनुरोधों के लिए अधिक काम करता है।

import lombok.SneakyThrows;
import java.util.concurrent.TimeUnit;

class LeakyBucket {
  private long minTimeNano; // sec / billion
  private long sched = System.nanoTime();

  /**
   * Create a rate limiter using the leakybucket alg.
   * @param perSec the number of requests per second
   */
  public LeakyBucket(double perSec) {
    if (perSec <= 0.0) {
      throw new RuntimeException("Invalid rate " + perSec);
    }
    this.minTimeNano = (long) (1_000_000_000.0 / perSec);
  }

  @SneakyThrows public void consume() {
    long curr = System.nanoTime();
    long timeLeft;

    synchronized (this) {
      timeLeft = sched - curr + minTimeNano;
      sched += minTimeNano;
    }
    if (timeLeft <= minTimeNano) {
      return;
    }
    TimeUnit.NANOSECONDS.sleep(timeLeft);
  }
}

और ऊपर के लिए सबसे उपयुक्त:

import com.google.common.base.Stopwatch;
import org.junit.Ignore;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class LeakyBucketTest {
  @Test @Ignore public void t() {
    double numberPerSec = 10000;
    LeakyBucket b = new LeakyBucket(numberPerSec);
    Stopwatch w = Stopwatch.createStarted();
    IntStream.range(0, (int) (numberPerSec * 5)).parallel().forEach(
        x -> b.consume());
    System.out.printf("%,d ms%n", w.elapsed(TimeUnit.MILLISECONDS));
  }
}

minTimeNanoयहाँ क्या मतलब है? क्या तुम समझा सकते हो?
फ्लैश

0

यहाँ सरल दर सीमक का थोड़ा उन्नत संस्करण है

/**
 * Simple request limiter based on Thread.sleep method.
 * Create limiter instance via {@link #create(float)} and call {@link #consume()} before making any request.
 * If the limit is exceeded cosume method locks and waits for current call rate to fall down below the limit
 */
public class RequestRateLimiter {

    private long minTime;

    private long lastSchedAction;
    private double avgSpent = 0;

    ArrayList<RatePeriod> periods;


    @AllArgsConstructor
    public static class RatePeriod{

        @Getter
        private LocalTime start;

        @Getter
        private LocalTime end;

        @Getter
        private float maxRate;
    }


    /**
     * Create request limiter with maxRate - maximum number of requests per second
     * @param maxRate - maximum number of requests per second
     * @return
     */
    public static RequestRateLimiter create(float maxRate){
        return new RequestRateLimiter(Arrays.asList( new RatePeriod(LocalTime.of(0,0,0),
                LocalTime.of(23,59,59), maxRate)));
    }

    /**
     * Create request limiter with ratePeriods calendar - maximum number of requests per second in every period
     * @param ratePeriods - rate calendar
     * @return
     */
    public static RequestRateLimiter create(List<RatePeriod> ratePeriods){
        return new RequestRateLimiter(ratePeriods);
    }

    private void checkArgs(List<RatePeriod> ratePeriods){

        for (RatePeriod rp: ratePeriods ){
            if ( null == rp || rp.maxRate <= 0.0f || null == rp.start || null == rp.end )
                throw new IllegalArgumentException("list contains null or rate is less then zero or period is zero length");
        }
    }

    private float getCurrentRate(){

        LocalTime now = LocalTime.now();

        for (RatePeriod rp: periods){
            if ( now.isAfter( rp.start ) && now.isBefore( rp.end ) )
                return rp.maxRate;
        }

        return Float.MAX_VALUE;
    }



    private RequestRateLimiter(List<RatePeriod> ratePeriods){

        checkArgs(ratePeriods);
        periods = new ArrayList<>(ratePeriods.size());
        periods.addAll(ratePeriods);

        this.minTime = (long)(1000.0f / getCurrentRate());
        this.lastSchedAction = System.currentTimeMillis() - minTime;
    }

    /**
     * Call this method before making actual request.
     * Method call locks until current rate falls down below the limit
     * @throws InterruptedException
     */
    public void consume() throws InterruptedException {

        long timeLeft;

        synchronized(this) {
            long curTime = System.currentTimeMillis();

            minTime = (long)(1000.0f / getCurrentRate());
            timeLeft = lastSchedAction + minTime - curTime;

            long timeSpent = curTime - lastSchedAction + timeLeft;
            avgSpent = (avgSpent + timeSpent) / 2;

            if(timeLeft <= 0) {
                lastSchedAction = curTime;
                return;
            }

            lastSchedAction = curTime + timeLeft;
        }

        Thread.sleep(timeLeft);
    }

    public synchronized float getCuRate(){
        return (float) ( 1000d / avgSpent);
    }
}

और इकाई परीक्षण

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class RequestRateLimiterTest {


    @Test(expected = IllegalArgumentException.class)
    public void checkSingleThreadZeroRate(){

        // Zero rate
        RequestRateLimiter limiter = RequestRateLimiter.create(0);
        try {
            limiter.consume();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void checkSingleThreadUnlimitedRate(){

        // Unlimited
        RequestRateLimiter limiter = RequestRateLimiter.create(Float.MAX_VALUE);

        long started = System.currentTimeMillis();
        for ( int i = 0; i < 1000; i++ ){

            try {
                limiter.consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long ended = System.currentTimeMillis();
        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( ((ended - started) < 1000));
    }

    @Test
    public void rcheckSingleThreadRate(){

        // 3 request per minute
        RequestRateLimiter limiter = RequestRateLimiter.create(3f/60f);

        long started = System.currentTimeMillis();
        for ( int i = 0; i < 3; i++ ){

            try {
                limiter.consume();
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long ended = System.currentTimeMillis();

        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( ((ended - started) >= 60000 ) & ((ended - started) < 61000));
    }



    @Test
    public void checkSingleThreadRateLimit(){

        // 100 request per second
        RequestRateLimiter limiter = RequestRateLimiter.create(100);

        long started = System.currentTimeMillis();
        for ( int i = 0; i < 1000; i++ ){

            try {
                limiter.consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long ended = System.currentTimeMillis();

        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( (ended - started) >= ( 10000 - 100 ));
    }

    @Test
    public void checkMultiThreadedRateLimit(){

        // 100 request per second
        RequestRateLimiter limiter = RequestRateLimiter.create(100);
        long started = System.currentTimeMillis();

        List<Future<?>> tasks = new ArrayList<>(10);
        ExecutorService exec = Executors.newFixedThreadPool(10);

        for ( int i = 0; i < 10; i++ ) {

            tasks.add( exec.submit(() -> {
                for (int i1 = 0; i1 < 100; i1++) {

                    try {
                        limiter.consume();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }) );
        }

        tasks.stream().forEach( future -> {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        long ended = System.currentTimeMillis();
        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
    }

    @Test
    public void checkMultiThreaded32RateLimit(){

        // 0,2 request per second
        RequestRateLimiter limiter = RequestRateLimiter.create(0.2f);
        long started = System.currentTimeMillis();

        List<Future<?>> tasks = new ArrayList<>(8);
        ExecutorService exec = Executors.newFixedThreadPool(8);

        for ( int i = 0; i < 8; i++ ) {

            tasks.add( exec.submit(() -> {
                for (int i1 = 0; i1 < 2; i1++) {

                    try {
                        limiter.consume();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }) );
        }

        tasks.stream().forEach( future -> {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        long ended = System.currentTimeMillis();
        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
    }

    @Test
    public void checkMultiThreadedRateLimitDynamicRate(){

        // 100 request per second
        RequestRateLimiter limiter = RequestRateLimiter.create(100);
        long started = System.currentTimeMillis();

        List<Future<?>> tasks = new ArrayList<>(10);
        ExecutorService exec = Executors.newFixedThreadPool(10);

        for ( int i = 0; i < 10; i++ ) {

            tasks.add( exec.submit(() -> {

                Random r = new Random();
                for (int i1 = 0; i1 < 100; i1++) {

                    try {
                        limiter.consume();
                        Thread.sleep(r.nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }) );
        }

        tasks.stream().forEach( future -> {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        long ended = System.currentTimeMillis();
        System.out.println( "Current rate:" + limiter.getCurRate() );
        Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
    }

}

कोड बहुत सरल है। आप बस अधिकतम सीमा या अवधि और दर के साथ सीमक बनाएँ। और फिर बस हर अनुरोध का उपभोग करें। जब भी दर को पार नहीं किया जाता है, तो सीमांत तुरंत वापस आ जाता है या कम वर्तमान अनुरोध दर पर लौटने से पहले कुछ समय इंतजार करता है। इसमें वर्तमान दर विधि भी है जो वर्तमान दर के औसत को खिसकाती है।
लियोनिद अस्तखोव

0

मेरा समाधान: एक सरल उपयोग विधि, आप इसे एक आवरण वर्ग बनाने के लिए संशोधित कर सकते हैं।

public static Runnable throttle (Runnable realRunner, long delay) {
    Runnable throttleRunner = new Runnable() {
        // whether is waiting to run
        private boolean _isWaiting = false;
        // target time to run realRunner
        private long _timeToRun;
        // specified delay time to wait
        private long _delay = delay;
        // Runnable that has the real task to run
        private Runnable _realRunner = realRunner;
        @Override
        public void run() {
            // current time
            long now;
            synchronized (this) {
                // another thread is waiting, skip
                if (_isWaiting) return;
                now = System.currentTimeMillis();
                // update time to run
                // do not update it each time since
                // you do not want to postpone it unlimited
                _timeToRun = now+_delay;
                // set waiting status
                _isWaiting = true;
            }
            try {
                Thread.sleep(_timeToRun-now);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // clear waiting status before run
                _isWaiting = false;
                // do the real task
                _realRunner.run();
            }
        }};
    return throttleRunner;
}

JAVA थ्रेड डेब्यू और थ्रॉटल से लें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.