This repository has been archived by the owner on Aug 9, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 73
API request limiter and queue #77
Labels
Comments
You can be banned if you exceed your rate limits constantly, but if you back off for the amount of time specified in the retry after header. You could go fancier using the Rate Limit Count Header too, but unless you're running something intense that requires the utmost optimization on your end, it is very likely overkill. |
Actually, I'm using quite intensively the API in batch mode, querying many game data etc. package me.biru.labs.service;
import me.biru.labs.util.Utils;
import net.rithms.riot.api.RiotApi;
import net.rithms.riot.api.RiotApiException;
import net.rithms.riot.dto.Game.RecentGames;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
/**
* Wrapper for the Riot API counting requests and limiting it withing the rate limit.
* This will prevent an overuse of the API and receiving "forbidden" status from the API.
*
* @author Brice Ambrosiak
*/
@Service
public class RiotApiWrapper {
private final Logger log = LoggerFactory.getLogger(this.getClass());
/** Riot API service */
private RiotApi riotApi;
/** Rate limits to the Riot API */
private List<RateLimitDto> rateLimits;
@PostConstruct
public void init() {
rateLimits = new ArrayList<>();
rateLimits.add(new RateLimitDto(10, TimeUnit.SECONDS, 10));
rateLimits.add(new RateLimitDto(500, TimeUnit.MINUTES, 10));
}
//TODO Implement all API requests
public RecentGames getRecentGames(long summonerId) throws RiotApiException {
registerRequest();
return riotApi.getRecentGames(summonerId);
}
public long fakeRequest() {
log.info("Fake request.");
registerRequest();
return System.currentTimeMillis();
}
/**
* Registers the request made to the API
* Waits until the request can be sent (within rate limits) and will sleep 1 second between each wait
* Also update and clean the queues
*/
private void registerRequest() {
log.debug("Registering request...");
// Wait until request can be made
while (!canSendRequest()) {
Utils.sleep(1);
log.debug("Waiting 1 second...");
}
// Add the new request as timestamp
long t = System.currentTimeMillis();
for (RateLimitDto rateLimit : rateLimits) {
rateLimit.getLastRequests().add(t);
}
log.debug("Request registered.");
}
/**
* Checks if the request can be sent to the API, and cleans old requests
* Will check each rate limit declared
*
* @return True if the request can be sent
*/
private boolean canSendRequest() {
log.debug("Checking if the request can be sent...");
long tNow = System.currentTimeMillis();
for (RateLimitDto rateLimit : rateLimits) {
cleanOldRequests(tNow, rateLimit);
}
return checkRateLimits();
}
/**
* Cleans old requests of the given rate limit, given the current timestamp considered
*
* @param tNow Current timestamp
* @param rateLimit Rate limit for which to clean old out-dated requests
*/
private void cleanOldRequests(long tNow, RateLimitDto rateLimit) {
// Clean old requests
log.debug("{} hits in the last {}. Cleaning old requests...", rateLimit.getLastRequests().size(), rateLimit);
boolean endLoop = false;
while (!rateLimit.getLastRequests().isEmpty() && !endLoop) {
long tLastRequest = rateLimit.getLastRequests().peek();
long tDiff = rateLimit.getTimeUnit().convert(tNow - tLastRequest, TimeUnit.MILLISECONDS);
// It has been more than 10 seconds since last request was done, clean it
if (tDiff > rateLimit.getIntervalInGivenTimeUnit()) {
long tClean = rateLimit.getLastRequests().poll();
log.debug("Cleaned request {} ({} {} ago).", tClean, tDiff, rateLimit.getTimeUnit());
} else {
// We are trying to do a request withing the 10s interval, it's still forbidden, nothing to clean
endLoop = true;
}
}
}
/**
* Check all rate limits defined, and tell if they are over or not
*
* @return False at the first rate limit exceeded, True otherwise
*/
private boolean checkRateLimits() {
for (RateLimitDto rateLimit : rateLimits) {
log.debug("Checking {} limit...", rateLimit);
if (rateLimit.getLastRequests().size() >= rateLimit.getMaxRequests()) {
log.debug("{} limit exceeded.", rateLimit);
return false;
}
}
log.debug("Rate limits are OK.");
return true;
}
}
/**
* Data object holding a rate limit information
*/
class RateLimitDto {
/** Queue storing all previous requests made to the API */
private Queue<Long> lastRequests;
/** Maximum number of requests that can be made in the given interval */
private int maxRequests;
/** Time unit of the interval */
private TimeUnit timeUnit;
/** Interval for which the maximum number of requests are counted, in time unit */
private int intervalInGivenTimeUnit;
public RateLimitDto(int maxRequests, TimeUnit timeUnit, int intervalInGivenTimeUnit) {
this.lastRequests = new LinkedList<>();
this.maxRequests = maxRequests;
this.timeUnit = timeUnit;
this.intervalInGivenTimeUnit = intervalInGivenTimeUnit;
}
public Queue<Long> getLastRequests() {
return lastRequests;
}
public void setLastRequests(Queue<Long> lastRequests) {
this.lastRequests = lastRequests;
}
public int getMaxRequests() {
return maxRequests;
}
public void setMaxRequests(int maxRequests) {
this.maxRequests = maxRequests;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
public void setTimeUnit(TimeUnit timeUnit) {
this.timeUnit = timeUnit;
}
public int getIntervalInGivenTimeUnit() {
return intervalInGivenTimeUnit;
}
public void setIntervalInGivenTimeUnit(int intervalInGivenTimeUnit) {
this.intervalInGivenTimeUnit = intervalInGivenTimeUnit;
}
@Override
public String toString() {
return getIntervalInGivenTimeUnit() + " " + getTimeUnit();
}
} |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
I've just discovered some limitation in the Riot API, which is that you can be banned temporarily/permanently if you hit the number of requests limit on your API key (I thought they would just say "Forbidden, try later").
I will then have to implement some sort of queue, in which all the API requests will be put before even contacting your library (some kind of proxy).
I was wondering in which measure your library could already implement a queue, with some initial methods to parametrize the user's key limits (X requests per 10s, Y requests per 10m, ...) ? (or even auto-discovery with X-Rate-Limit headers returned by the API).
The text was updated successfully, but these errors were encountered: