Details
-
New Feature
-
Status: Closed
-
Minor
-
Resolution: Complete
-
None
Description
PoC:
public class RateLimiter { private static final Log logger = LogFactory.getLog(RateLimiter.class); private final long interval; private final long maxDelay; private final AtomicLong nextRelease = new AtomicLong(0L); /** * Create an instance with the provided arguments. * @param rate the rate per time unit. * @param maxDelay the max allowable delay in time units. * @param unit the time unit. */ public RateLimiter(int rate, int maxDelay, TimeUnit unit) { this.interval = unit.toMillis(1) / rate; this.maxDelay = unit.toMillis(maxDelay); } public void delayIfNecessary() throws InterruptedException { long timeToWait = 0L; while (timeToWait == 0L) { long now = System.currentTimeMillis(); long next = this.nextRelease.getAndAdd(this.interval); if (next < now) { if (this.nextRelease.compareAndSet(next + this.interval, now + this.interval)) { logger.info("Sleeping for 0"); return; } else { logger.info("nextRelease has been changed by another thread"); } } else { timeToWait = next - now; } } if (timeToWait > this.maxDelay) { throw new IllegalStateException("Rate limiter maxDelay exceeded"); } logger.info("Sleeping for " + timeToWait); Thread.sleep(timeToWait); } public static void main(String[] args) throws Exception { RateLimiter rl = new RateLimiter(2, 5, TimeUnit.SECONDS); ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 15; i++) { final int j = i; exec.execute(() -> { StopWatch watch = new StopWatch("task" + j); watch.start(); try { rl.delayIfNecessary(); } catch (IllegalStateException | InterruptedException e) { logger.info("delay too long for task" + j); } watch.stop(); logger.info(watch.prettyPrint()); }); } exec.shutdown(); exec.awaitTermination(60, TimeUnit.SECONDS); } }
2018-07-01 14:19:51,697 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-5] : Sleeping for 500 2018-07-01 14:19:51,705 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-7] : Sleeping for 2499 2018-07-01 14:19:51,705 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-1] : nextRelease has been changed by another thread 2018-07-01 14:19:51,699 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-4] : Sleeping for 1499 2018-07-01 14:19:51,703 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-11] : Sleeping for 4497 2018-07-01 14:19:51,707 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-9] : Sleeping for 3497 2018-07-01 14:19:51,703 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-6] : Sleeping for 1999 2018-07-01 14:19:51,697 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-15] : delay too long for task14 2018-07-01 14:19:51,707 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-1] : delay too long for task0 2018-07-01 14:19:51,697 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-8] : Sleeping for 2998 2018-07-01 14:19:51,702 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-10] : Sleeping for 3997 2018-07-01 14:19:51,699 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-3] : Sleeping for 999 2018-07-01 14:19:51,699 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-14] : delay too long for task13 2018-07-01 14:19:51,699 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-13] : delay too long for task12 2018-07-01 14:19:51,698 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-2] : Sleeping for 0 2018-07-01 14:19:51,700 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-12] : Sleeping for 4997 2018-07-01 14:19:51,710 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-15] : StopWatch 'task14': running time (millis) = 16 ----------------------------------------- ms % Task name ----------------------------------------- 00016 100% 2018-07-01 14:19:51,710 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-14] : StopWatch 'task13': running time (millis) = 17 ----------------------------------------- ms % Task name ----------------------------------------- 00017 100% 2018-07-01 14:19:51,710 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-1] : StopWatch 'task0': running time (millis) = 20 ----------------------------------------- ms % Task name ----------------------------------------- 00020 100% 2018-07-01 14:19:51,710 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-2] : StopWatch 'task1': running time (millis) = 21 ----------------------------------------- ms % Task name ----------------------------------------- 00021 100% 2018-07-01 14:19:51,711 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-13] : StopWatch 'task12': running time (millis) = 17 ----------------------------------------- ms % Task name ----------------------------------------- 00017 100% 2018-07-01 14:19:52,208 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-5] : StopWatch 'task4': running time (millis) = 521 ----------------------------------------- ms % Task name ----------------------------------------- 00521 100% 2018-07-01 14:19:52,713 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-3] : StopWatch 'task2': running time (millis) = 1024 ----------------------------------------- ms % Task name ----------------------------------------- 01024 100% 2018-07-01 14:19:53,209 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-4] : StopWatch 'task3': running time (millis) = 1520 ----------------------------------------- ms % Task name ----------------------------------------- 01520 100% 2018-07-01 14:19:53,712 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-6] : StopWatch 'task5': running time (millis) = 2023 ----------------------------------------- ms % Task name ----------------------------------------- 02023 100% 2018-07-01 14:19:54,211 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-7] : StopWatch 'task6': running time (millis) = 2522 ----------------------------------------- ms % Task name ----------------------------------------- 02522 100% 2018-07-01 14:19:54,711 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-8] : StopWatch 'task7': running time (millis) = 3022 ----------------------------------------- ms % Task name ----------------------------------------- 03022 100% 2018-07-01 14:19:55,210 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-9] : StopWatch 'task8': running time (millis) = 3519 ----------------------------------------- ms % Task name ----------------------------------------- 03519 100% 2018-07-01 14:19:55,710 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-10] : StopWatch 'task9': running time (millis) = 4020 ----------------------------------------- ms % Task name ----------------------------------------- 04020 100% 2018-07-01 14:19:56,208 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-11] : StopWatch 'task10': running time (millis) = 4518 ----------------------------------------- ms % Task name ----------------------------------------- 04518 100% 2018-07-01 14:19:56,711 INFO org.springframework.integration.support.RateLimiter [pool-2-thread-12] : StopWatch 'task11': running time (millis) = 5020 ----------------------------------------- ms % Task name ----------------------------------------- 05020 100%