How can I queue up and delay retrofit requests to avoid hitting an api rate limit?
Asked Answered
C

3

14

I'm using an api that implements throttling. One of the limits is 1 request/second. ugh. I have the following scenario which hits the limit right away.

  • Check the status of the api with api/status

  • if the api is up, get a users subscriptions

  • load a page from the list of subscriptions

Is there anything I can plug into retrofit that can queue each network request to only run at least 1000ms after the last? I am using/learning rxjava, can debounce be of any use here?

Clausius answered 24/12, 2016 at 0:0 Comment(0)
P
15

You can throttle your observable.

    Observable<String> text = ...
text.throttleLast(1, SECONDS)
    .flatMap(retrofitApiCall())
    .subscribe(result -> System.out.println("result: " + result));

Another solution is to set a dispatcher in your okhttp builder, and add an interceptor that sleeps for one second. This may not be the most elegant solution and kills some of the benefits of using async because it limits you to one thread at a time.

OkHttpClient.Builder builder = new OkHttpClient.Builder();


    Dispatcher dispatcher = new Dispatcher();
    dispatcher.setMaxRequests(1);

    Interceptor interceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            SystemClock.sleep(1000);
            return chain.proceed(chain.request());
        }
    };

    builder.addNetworkInterceptor(interceptor);
    builder.dispatcher(dispatcher);
    builder.build();
Pawsner answered 9/3, 2017 at 6:26 Comment(4)
What's the difference between this and calling debounce() ?Clausius
throttle will simply avoid 2 items to be emitted too quickly, by delaying the 2nd emission, if it is too close to the 1st one. debounce will drop the 1st emission if a 2nd emission arrives before a given amount of time. In the worst case with debounce no item will ever be emitted, because emissions are too quick w.r.t. the debounce timeEbner
This was definitely the solution. Thanks I did not know that interceptors add the delay logic and dispatcher add the retry logicManofwar
It seems like the Interceptor will unconditionally delay -- that does solve the throttling issue, but there is no reason to delay if the app is otherwise behaving itself...Cordeiro
N
7

An interceptor (from OkHttpClient) combined with a RateLimiter (from Guava) is a good solution to avoid HTTP 429 error code.

Let's suppose we want a limit of 3 calls per second:

import java.io.IOException;

import com.google.common.util.concurrent.RateLimiter;

import okhttp3.Interceptor;
import okhttp3.Response;

public class RateLimitInterceptor implements Interceptor {
    private RateLimiter limiter = RateLimiter.create(3);

    @Override
    public Response intercept(Chain chain) throws IOException {
        limiter.acquire(1);
        return chain.proceed(chain.request());
    }
}
Necrophilism answered 12/9, 2019 at 16:29 Comment(0)
A
0

Actually, you can use already made OkHttp Delay Interceptor library. Just call .addInterceptor(DelayInterceptor(Long, TimeUnit)) method on your's OkHttp client builder object.

Aperient answered 12/6, 2022 at 16:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.