Reputation: 129
Has OkHttp an integrated way of complying with an api rate request limit, or it has to be implemented externally? either case a hint on where to start is appreciated.
Upvotes: 8
Views: 7385
Reputation: 15689
An interceptor combined with a RateLimiter from Guava was a good solution to avoid receiving a 429 HTTP 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 rateLimiter = RateLimiter.create(3);
@Override
public Response intercept(Chain chain) throws IOException {
rateLimiter.acquire(1);
return chain.proceed(chain.request());
}
}
Upvotes: 9
Reputation: 19
I also have the problem. I want limit rate when upload large file by post. I read OkHttp Interceptors code. And find can limite body write to limit upload rate.
public class RateLimitingRequestBody extends RequestBody {
private MediaType mContentType;
private File mFile;
private int mMaxRate; // ms/bit
private RateLimitingRequestBody(@Nullable final MediaType contentType, final File file, int rate){
mContentType = contentType;
mFile = file;
mMaxRate = rate;
}
@Override
public MediaType contentType() {
return mContentType;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(mFile);
writeAll(sink, source);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Util.closeQuietly(source);
}
}
public long writeAll(BufferedSink sink, Source source) throws IOException, InterruptedException {
if (source == null) {
throw new IllegalArgumentException("source == null");
} else {
long totalBytesRead = 0L;
long readCount;
long start = System.currentTimeMillis();
while((readCount = source.read(sink.buffer(), 8192L)) != -1L) {
totalBytesRead += readCount;
sink.emitCompleteSegments();
long time = System.currentTimeMillis();
if(time == start) continue;
long rate = (totalBytesRead * 8) / (time - start);
NLog.v("writeAll","totalBytesRead:"+totalBytesRead+"B "+ " Rate:"+rate*1000+"bits");
if(rate > mMaxRate/1000){
int sleep = (int) (totalBytesRead * 8 * 1000 / mMaxRate - (time - start));
NLog.d("writeAll", "sleep:"+sleep);
Thread.sleep(sleep+50);
}
}
long end = System.currentTimeMillis();
long rate = (totalBytesRead * 8 * 1000) / ((end - start));
NLog.e("writeAll","totalBytesRead:"+totalBytesRead+"B "+ " Rate:"+rate+"bits"+" total time:"+(end-start));
return totalBytesRead;
}
}
public static RequestBody createRequestBody(@Nullable final MediaType contentType, final File file, int rate) {
if (file == null) {
throw new NullPointerException("content == null");
} else {
return new RateLimitingRequestBody(contentType, file, rate);
}
}
}
May be this can help you.
Upvotes: 1
Reputation: 711
As @jesse-wilson said, you can do this with OkHttp Interceptors
Here's an example. First define a custom Interceptor. The api I call responds with HTTP Code 429 when the rate limit is hit. You will need to check for the particular HTTP Code or Header in your own api that indicates a rate error, and sleep for an appropriate time.
public class RateLimitInterceptor implements Interceptor {
public RateLimitInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
// 429 is how the api indicates a rate limit error
if (!response.isSuccessful() && response.code() == 429) {
System.err.println("Cloudant: "+response.message());
// wait & retry
try {
System.out.println("wait and retry...");
Thread.sleep(1000);
} catch (InterruptedException e) {}
response = chain.proceed(chain.request());
}
return response;
}
}
Next add the Interceptor to where you build the OkHttp request. Here's an example of my builder...
public static Response fetchPaged(HttpUrl url) throws IOException {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new BasicAuthInterceptor(username, password))
.addInterceptor(new RateLimitInterceptor())
.build();
Request request = new Request.Builder()
.url(url)
.build();
return client
.newCall(request)
.execute();
}
Upvotes: 8
Reputation: 40593
You can build an interceptor to track requests made, and potentially throttle or fail requests if the rate is too high.
Upvotes: 1