/******************************************************************************* * Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package org.cloudifysource.esc.driver.provisioning; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import com.google.common.util.concurrent.RateLimiter; /** * This rate limiter is designed to allow a specific number of request attempts in a defined period of time. * in-case the request limit exceeds, block method will block the current thread until the cool-down * period has ended. The implementation uses the {@link com.google.common.util.concurrent.RateLimiter} * to determine the actual cool-down period of the throttling according to the time frame specified. * * * @author adaml * @since 2.7.0 * */ public class RequestRateLimiter { private final int numRequests; private final RateLimiter throttler; private final Logger logger = Logger.getLogger(RequestRateLimiter.class.getName()); private int requestCounter; /** * * @param numRequests - the number of allowed requests in the given time frame. * @param timeFrame - the timeframe limit for number of requests * @param unit - time unit. */ public RequestRateLimiter(final int numRequests, final long timeFrame, final TimeUnit unit) { this.numRequests = numRequests; this.throttler = RateLimiter.create(1.0 / unit.toSeconds(timeFrame)); } /** * initializes the request rate limiter upon first block request in rotation. */ private void init() { boolean acquired = this.throttler.tryAcquire(); if (acquired) { this.throttler.acquire(); } } /** * * @return true if blocking was preformed. */ public boolean block() { incrementRetryCounter(); if (isInitRequired()) { init(); } if (isThrottlingRequired()) { logger.fine("Request limit has been reached. Rate limit is being enforced."); throttler.acquire(); logger.fine("Rate limit enforcement has ended."); return true; } return false; } // is this the first block attempt in the rotation? private boolean isInitRequired() { if ((this.requestCounter % this.getNumRequests()) == 1) { return true; } return false; } /** * * @return true if blocking is required. */ public boolean tryBlock() { return isThrottlingRequired(); } private boolean isThrottlingRequired() { if ((this.requestCounter % this.getNumRequests()) == 0) { return true; } return false; } private synchronized void incrementRetryCounter() { this.requestCounter = this.requestCounter + 1; } /** * Returns the defined duration for consequtive requests. * * @return the defined duration for consequtive requests. */ public long getDuration() { return (long) (1.0 / this.throttler.getRate()); } public int getNumRequests() { return numRequests; } /** * returns the remaining retries until block. * * @return * remaining retries until block. */ public int getRemainingRetries() { return numRequests - (this.requestCounter % numRequests); } }