থ্রোটলিং পদ্ধতিটি এন সেকেন্ডে এম অনুরোধগুলিতে কল করে


137

আমার এমন একটি উপাদান / শ্রেণি প্রয়োজন যা কিছু পদ্ধতির সম্পাদনকে সর্বাধিক এম কলগুলিতে এন সেকেন্ডে (বা এমএস বা ন্যানোসের কোনও ব্যাপার না) নিয়ে আসে thr

অন্য কথায় আমার নিশ্চিত হওয়া দরকার যে আমার পদ্ধতিটি এন সেকেন্ডের একটি সহচরী উইন্ডোতে এম-এর চেয়ে বেশি সময় নির্বাহ করা হয় না।

আপনি যদি না জানেন তবে বিদ্যমান শ্রেণি নির্দ্বিধায় আপনার সমাধান / ধারণা পোস্ট করতে পারেন আপনি কীভাবে এটি বাস্তবায়ন করবেন।



3
সেখানে এই সমস্যার কিছু মহান উত্তর stackoverflow.com/questions/667508/...
skaffman

> আমার নিশ্চিত করা দরকার যে আমার পদ্ধতিটি>> এন সেকেন্ডের স্লাইডিং উইন্ডোতে এম বারের চেয়ে বেশি কার্যকর হয় না। কীভাবে। নেট এ এটি করা যায় সে সম্পর্কে আমি সম্প্রতি একটি ব্লগ পোস্ট লিখেছিলাম। আপনি জাভাতে অনুরূপ কিছু তৈরি করতে সক্ষম হতে পারেন। নেট এ আরও ভাল হার সীমাবদ্ধ
জ্যাক লিচ

মূল প্রশ্নটি এই ব্লগ পোস্টে সমস্যার সমাধানের মতো অনেকটা মনে হচ্ছে: [জাভা মাল্টি-চ্যানেল অ্যাসিনক্রোনাস থ্রোটলার ] ( কর্ডিন.কম / ব্লগ / ২০১০ / ২০১৮ / জাভা- মাল্টিচ্যানেল- অ্যাসিঙ্ক্রোনাস । Html )। এন সেকেন্ডে এম কলের হারের জন্য, এই ব্লগে আলোচিত থ্রোটলার গ্যারান্টি দেয় যে টাইমলাইনে N এর দৈর্ঘ্যের যে কোনও বিরতি এম কলের বেশি থাকবে না।
এইচবিএফ मे

উত্তর:


81

আমি একটি নির্দিষ্ট আকারের এম এর সাথে টাইমস্ট্যাম্পগুলির একটি রিং বাফার ব্যবহার করতাম, যতবার পদ্ধতিটি বলা হয় আপনি প্রাচীনতম এন্ট্রিটি পরীক্ষা করে দেখেন এবং যদি এটি অতীতে এন সেকেন্ডের চেয়ে কম হয়, আপনি এক্সিকিউট করে অন্য এন্ট্রি যুক্ত করেন, অন্যথায় আপনি ঘুমান সময়ের পার্থক্য জন্য।


4
বাহ। আমার যা দরকার ঠিক তাই এই এবং ন্যূনতম মেমরির পদক্ষেপটি বাস্তবায়নের জন্য দ্রুত প্রচেষ্টা 10 ডলার লাইন দেখায়। থ্রেড সুরক্ষা এবং আগত অনুরোধগুলির সারিবদ্ধতা সম্পর্কে কেবল চিন্তা করা দরকার।
vtrubnikov

5
এজন্য আপনি java.util.concurrent থেকে DelayQueue ব্যবহার করেন। এটি একই প্রবেশে একাধিক থ্রেডের অভিনয় রোধ করে preven
ইরিকসন

5
বহু থ্রেডেড কেসের ক্ষেত্রে টোকেন বালতি পদ্ধতির আরও ভাল পছন্দ হতে পারে, আমি মনে করি।
মাইকেল বর্গওয়ার্ট

1
আপনি কি জানেন যে এই অ্যালগরিদমটির কোনও নাম থাকলে কীভাবে ডাকা হয়?
ভ্লাদো পান্ডিয়াস

80

আমার জন্য বাক্সটির বাইরে যা কাজ করেছিল তা হ'ল গুগল পেয়ারা রেটলিমিটার

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

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

19
আমি এই সমাধানটির সুপারিশ করব না কারণ পেয়ারা রেটলিমিটার থ্রেডটি ব্লক করবে এবং এটি থ্রেড পুলটি সহজেই নিঃশেষ করবে।
কাবিডিস

18
@ কাভিডিস আপনি যদি অবরুদ্ধ করতে না চান তবে ব্যবহার করুনtryAquire()
slf

7
রেটলিমিটারের বর্তমানে প্রয়োগের ক্ষেত্রে (কমপক্ষে আমার জন্য) সমস্যাটি হ'ল এটি 1 সেকেন্ডের বেশি সময়ের সময়কালের জন্য মঞ্জুরি দেয় না এবং তাই প্রতি মিনিটে উদাহরণস্বরূপ 1 এর হারগুলি।
জন বি

4
@ জন বি যতটুকু আমি বুঝতে পেরেছি, আপনি রেটলিমিটার ক্রিয়েট (60.0) + রেটলিমিটার.অ্যাকুইয়ার (60)
বিভাজ্য জিরো

2
@radiantRazor রেটলিমিটার.ক্রিয়েট (1.0 / 60) এবং অর্জন () প্রতি মিনিটে 1 কল অর্জন করে।
বাইজেন্টাস

30

কংক্রিট কথায়, আপনি এটি দিয়ে প্রয়োগ করতে সক্ষম হবেন DelayQueueM Delayedপ্রাথমিকভাবে শূন্যে সেট হয়ে দেরিতে তাদের দৃষ্টান্ত দিয়ে কাত শুরু করুন । পদ্ধতিটিতে অনুরোধগুলি আসার সাথে সাথে, takeএকটি টোকেন যা থ্রোটলিংয়ের প্রয়োজনীয়তা পূরণ না হওয়া অবধি পদ্ধতিটি অবরুদ্ধ করে। একটি টোকেন নেওয়া হয়ে গেলে, addবিলম্বের সাথে কাতারে একটি নতুন টোকেন N


1
হ্যাঁ, এটি কৌশলটি করবে। তবে আমি বিশেষত ডেলাকিউ পছন্দ করি না কারণ এটি (প্রিয়ার্তিকিউয়ের মাধ্যমে) একটি ভারসাম্য বাইনারি হ্যাশ (যার অর্থ প্রচুর তুলনা offerএবং সম্ভাব্য অ্যারে বৃদ্ধি) বোঝায় এবং এটি আমার জন্য খুব বেশি ভারী। আমি অনুমান করি অন্যের জন্য এটি পুরোপুরি ঠিক আছে।
vtrubnikov

5
আসলে, এই অ্যাপ্লিকেশনটিতে, যেহেতু গাদাতে যুক্ত হওয়া নতুন উপাদানটি প্রায় সর্বদা গাদাতে সর্বাধিক উপাদান হয়ে থাকে (অর্থাত্ দীর্ঘতম বিলম্ব হয়), সাধারণত অ্যাড প্রতি একটি তুলনা প্রয়োজন। এছাড়াও, অ্যালগরিদমটি সঠিকভাবে প্রয়োগ করা হলে অ্যারে কখনই বৃদ্ধি পাবে না, যেহেতু একটি উপাদান কেবলমাত্র একটি উপাদান গ্রহণের পরে যুক্ত করা হয়।
এরিকসন

3
আমি এই ক্ষেত্রে সহায়কটি পেয়েছি যেখানে আপনি বড় আকারের বিস্ফোরণগুলিতে আকারের এম রেখে মাইন্ডফুল মিলিসের তুলনায় এন তুলনামূলকভাবে ছোট করে দেরী করতে চান না। যেমন। এম = 5, এন = 20 মিমি 5 / আকারের ক্যাপিং ফেটে 250 / সেকেন্ডের মাধ্যমে সরবরাহ করবে
FUD

এই স্কেলটি কি এক মিলিয়ন আরপিএমের জন্য এবং যখন একযোগে অনুরোধগুলি অনুমোদিত হবে? আমার এক মিলিয়ন বিলম্বকারী উপাদানগুলি যুক্ত করা দরকার। এছাড়াও কোণার কেসগুলি বিলম্বিত্বে বেশি হবে - একাধিক থ্রেড পোল কল করছে এমন ক্ষেত্রে () এবং এটি প্রতিবার লক হয়ে থাকবে।
আদিত্য জোশি 13

@ আদিত্য জোশি আমি এটি বেঞ্চমার্ক করিনি, তবে কিছুটা সময় পেলে আমি ওভারহেডের ধারণাটি বোঝার চেষ্টা করব। যদিও একটি বিষয় লক্ষণীয় তা হল আপনার 1 মিলিয়ন টোকেনের প্রয়োজন নেই যা 1 সেকেন্ডে শেষ হয় ire আপনার কাছে ১০০ টোকেন থাকতে পারে যা 10 মিলি সেকেন্ডে শেষ হয়, 10 টোকেন যা মিলিসেকেন্ডে শেষ হয়, ইত্যাদি the হার সীমাবদ্ধ। যদিও 1 মিলিয়ন আরপিএম খুব কমই থ্রটলিংয়ের মতো শোনাচ্ছে। আপনি যদি আপনার ব্যবহারের বিষয়টি ব্যাখ্যা করতে পারেন তবে আমার আরও ভাল ধারণা থাকতে পারে।
এরিকসন

21

আপ পড়ুন টোকেন বালতি অ্যালগরিদম। মূলত, এটিতে টোকেন সহ আপনার একটি বালতি রয়েছে। যতবার আপনি এই পদ্ধতিটি কার্যকর করেন, আপনি একটি টোকেন নেন। যদি আরও টোকেন না থাকে তবে আপনি একটি না পাওয়া পর্যন্ত আপনি অবরুদ্ধ করে রাখুন। এদিকে, এমন কিছু বাহ্যিক অভিনেতা রয়েছেন যা একটি নির্দিষ্ট বিরতিতে টোকেনগুলি পূরণ করে।

এটি করার জন্য আমি কোনও গ্রন্থাগার সম্পর্কে সচেতন নই (বা অনুরূপ কিছু)। আপনি এই যুক্তিটি আপনার কোডটিতে লিখতে পারেন বা আচরণ যুক্ত করতে AspectJ ব্যবহার করতে পারেন।


3
পরামর্শের জন্য ধন্যবাদ, ইতিমধ্যে আকর্ষণীয়। তবে এটি আমার যা প্রয়োজন ঠিক তা নয়। উদাহরণস্বরূপ, আমার প্রতি সেকেন্ডে 5 টি কল প্রয়োগের সীমাবদ্ধ করতে হবে। যদি আমি টোকেন বালতি ব্যবহার করি এবং একই সাথে 10 টি অনুরোধ আসে তবে প্রথম 5 টি কল উপলব্ধ সমস্ত টোকেন গ্রহণ করে এবং মুহুর্তে সম্পাদন করে, বাকি 5 টি কল 1/3 s এর নির্দিষ্ট বিরতিতে কার্যকর করা হবে। এই পরিস্থিতিতে আমার কেবলমাত্র 1 সেকেন্ড পাসের পরে একক ফেটে মৃত্যুর জন্য আরও 5 টি কল প্রয়োজন need
vtrubnikov

5
আপনি যদি বালতিতে প্রতি সেকেন্ডে 5 / টোকেন যোগ করেন (বা 5 - (5-অবশিষ্ট) প্রতি 1/5 সেকেন্ডে 1 এর পরিবর্তে?
কেভিন

@ কেভিন না এটি এখনও আমাকে 'স্লাইডিং উইন্ডো' প্রভাব দেয় না
vtrubnikov

2
@ ভ্যালারি হ্যাঁ এটা হবে। (যদিও এম টোকেনগুলি ক্যাপ করার কথা মনে রাখবেন)
নং

"বাহ্যিক অভিনেতা" দরকার নেই। অনুরোধের সময়গুলি সম্পর্কে মেটাডেটা প্রায় রাখলে সবকিছু একত্রে থ্রেডড করা যায়।
মার্শেলাস ওয়ালেস

8

আপনার যদি জাভা ভিত্তিক স্লাইডিং উইন্ডো রেট সীমাবদ্ধতার প্রয়োজন হয় যা বিতরণ করা সিস্টেম জুড়ে কাজ করে তবে আপনি https://github.com/mokies/ratelimitj প্রকল্পটি একবার দেখে নিতে পারেন ।

রেডিস ব্যাকড কনফিগারেশন, আইপি দ্বারা প্রতি মিনিটে 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 দেখুন ।


5

এটি আবেদনের উপর নির্ভর করে।

কেস যা কল্পনা একাধিক থ্রেড একটি টোকেন চান কিছু করতে বিশ্বব্যাপী হার-সীমিত কর্ম সঙ্গে কোনো মঞ্জুরিপ্রাপ্ত ফেটে (অর্থাত আপনি 10 সেকেন্ডের 10 ক্রিয়া সীমিত করতে চাই কিন্তু 10 ক্রিয়া প্রথম দ্বিতীয় ঘটতে করতে চান না এবং তারপর থাকা 9 সেকেন্ড বন্ধ)।

বিলম্বিত কিউয়ের একটি অসুবিধা রয়েছে: যে থ্রেডগুলিতে টোকেনগুলি অনুরোধ করে তার ক্রমটি তারা অনুরোধটি পূরণ করার আদেশ হতে পারে না। যদি টোকেনের জন্য অপেক্ষা করে একাধিক থ্রেডগুলি অবরুদ্ধ করা হয় তবে এটি উপলব্ধ নয় যে পরবর্তী কোনটি টোকেন গ্রহণ করবে। এমনকি আপনি আমার দৃষ্টিতে থ্রেড চিরকাল অপেক্ষা করতে পারেন।

একটি সমাধান হ'ল ধারাবাহিকভাবে দুটি ক্রিয়াগুলির মধ্যে ন্যূনতম সময়ের ব্যবধান থাকা এবং তাদের অনুরোধ অনুসারে একই ক্রমে পদক্ষেপ নেওয়া।

এখানে একটি বাস্তবায়ন:

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পরবর্তী টোকেনটি গ্রাস করার আগে টোকন খাওয়ার পরে ন্যূনতম সময় পার হওয়া দরকার।
ডুয়ার্টে মেনেসিস

3

যদিও এটি আপনি ThreadPoolExecutorযা চেয়েছিলেন তা নয় , যা এম সেকেন্ডে এম অনুরোধগুলির পরিবর্তে এম যুগপত অনুরোধগুলি ক্যাপ করার জন্য তৈরি করা হয়েছে, এটিও কার্যকর হতে পারে।


2

আমি একটি সাধারণ থ্রোটলিং অ্যালগরিদম প্রয়োগ করেছি this এই লিঙ্কটি ব্যবহার করে দেখুন, http://krishnaprasadas.blogspot.in/2012/05/throttling-algorithm.html

অ্যালগরিদম সম্পর্কে একটি সংক্ষিপ্ত বিবরণ,

এই অ্যালগরিদম জাভার সামর্থ্য সদ্ব্যবহার বিলম্বিত সারি । প্রত্যাশিত বিলম্ব সহ একটি বিলম্বিত বস্তু তৈরি করুন (মিলিসেকেন্ড টাইমউনিতের জন্য এখানে 1000 / এম )। বিলম্বিত সারিতে একই বস্তুটি রাখুন যা আমাদের জন্য চলমান উইন্ডো সরবরাহ করবে। তারপর প্রতিটি পদ্ধতির কল করার আগে নিতে বস্তুর কিউ গঠন নিয়ে একটি অবরোধ কল যা শুধুমাত্র নির্দিষ্ট বিলম্বের পর ফিরে আসবে, এবং পদ্ধতি কল পর ভুলবেন না আপডেট সময়ের সাথে সাথে কিউ 'র মধ্যে বস্তুর লাগাতে (এখানে বর্তমান মিলিসেকেন্ড) ।

এখানে আমরা বিভিন্ন বিলম্বের সাথে একাধিক বিলম্বিত বস্তুও রাখতে পারি। এই পদ্ধতির এছাড়াও উচ্চ মাধ্যমে আউটপুট সরবরাহ করবে।


6
আপনার অ্যালগরিদমের একটি সারাংশ পোস্ট করা উচিত। যদি আপনার লিঙ্কটি চলে যায় তবে আপনার উত্তরটি অকেজো হয়ে যায়।
jwr

ধন্যবাদ, আমি সংক্ষিপ্ত যোগ করেছি।
কৃষাশ

1

নীচে আমার বাস্তবায়ন যথেচ্ছ অনুরোধের সময়ের যথাযথতা পরিচালনা করতে পারে, এতে প্রতিটি অনুরোধের জন্য ও (1) সময় জটিলতা রয়েছে, কোনও অতিরিক্ত বাফার প্রয়োজন নেই, যেমন ও (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

এটি উপরের লিকিবাকেট কোডের একটি আপডেট। এটি প্রতি সেকেন্ডে আরও 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;
}

জেভিএ থ্রেড ডিবাউন এবং থ্রোটল থেকে নিন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.