【发布时间】:2016-02-12 14:18:41
【问题描述】:
OkHttp 是否有一种符合 api 速率请求限制的集成方式,还是必须在外部实现?无论哪种情况,都可以提示从哪里开始。
【问题讨论】:
标签: okhttp rate rate-limiting
OkHttp 是否有一种符合 api 速率请求限制的集成方式,还是必须在外部实现?无论哪种情况,都可以提示从哪里开始。
【问题讨论】:
标签: okhttp rate rate-limiting
interceptor 结合来自 Guava 的 RateLimiter 是避免接收 429 HTTP 代码的好方法。
假设我们希望每秒调用 3 次:
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());
}
}
【讨论】:
正如@jesse-wilson 所说,您可以使用OkHttp Interceptors 做到这一点
这是一个例子。首先定义一个自定义拦截器。当达到速率限制时,我调用的 api 会以 HTTP 代码 429 响应。您需要在自己的 api 中检查指示速率错误的特定 HTTP 代码或标头,并休眠适当的时间。
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;
}
}
接下来将拦截器添加到您构建 OkHttp 请求的位置。这是我的构建器的示例...
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();
}
【讨论】:
您可以构建一个interceptor 来跟踪发出的请求,如果速率太高,可能会限制或失败请求。
【讨论】:
我也有这个问题。 通过邮寄上传大文件时,我想要限制速率。我阅读了 OkHttp 拦截器代码。并且 find 可以限制正文写入以限制上传速率。
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);
}
}
}
也许这对你有帮助。
【讨论】: