/** * @param qps the average qps desired. * @param fullRequestTimeoutMillis max time to fully satisfy a token request. This is generally a small timeout, on the * order of the network latency (e.g. ~100 ms). * @param maxBucketSizeMillis maximum number of unused tokens that can be stored during under-utilization time, in * milliseconds. The actual tokens stored will be 1000 * qps * maxBucketSizeMillis. */ DynamicTokenBucket(long qps, long fullRequestTimeoutMillis, long maxBucketSizeMillis) { this.tokenBucket = new TokenBucket(qps, maxBucketSizeMillis); this.baseTimeout = fullRequestTimeoutMillis; }
@Test public void testTimeout() throws Exception { TokenBucket tokenBucket = new TokenBucket(100, 0); // If it cannot satisfy the request within the timeout, return false immediately Assert.assertFalse(tokenBucket.getTokens(100, 1, TimeUnit.MILLISECONDS)); Assert.assertFalse(tokenBucket.getTokens(100, 10, TimeUnit.MILLISECONDS)); Assert.assertFalse(tokenBucket.getTokens(100, 100, TimeUnit.MILLISECONDS)); Assert.assertTrue(tokenBucket.getTokens(10, 101, TimeUnit.MILLISECONDS)); // Can use stored tokens to satisfy request tokenBucket = new TokenBucket(100, 100); Thread.sleep(200); // fill up bucket Assert.assertTrue(tokenBucket.getTokens(20, 101, TimeUnit.MILLISECONDS)); }
private void testForQps(long qps) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(10); TokenBucket tokenBucket = new TokenBucket(qps, 1000); List<Future<Boolean>> futures = Lists.newArrayList(); long permitsPerRequest = qps / 10; long start = System.currentTimeMillis(); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); futures.add(executorService.submit(new MyRunnable(tokenBucket, permitsPerRequest, 1000))); for (Future<Boolean> future : futures) { Assert.assertTrue(future.get()); } long end = System.currentTimeMillis(); double averageRate = 1000 * (double) (permitsPerRequest * futures.size()) / (end - start); Assert.assertTrue(Math.abs(averageRate - qps) / qps < 0.2, "Average rate: " + averageRate + " expected: 100"); }
/** * @param qps the average qps desired. * @param fullRequestTimeoutMillis max time to fully satisfy a token request. This is generally a small timeout, on the * order of the network latency (e.g. ~100 ms). * @param maxBucketSizeMillis maximum number of unused tokens that can be stored during under-utilization time, in * milliseconds. The actual tokens stored will be 1000 * qps * maxBucketSizeMillis. */ DynamicTokenBucket(long qps, long fullRequestTimeoutMillis, long maxBucketSizeMillis) { this.tokenBucket = new TokenBucket(qps, maxBucketSizeMillis); this.baseTimeout = fullRequestTimeoutMillis; }