OkHttp是否具有内置的符合API请求限制的方法,还是必须在外部实现?无论哪种情况,都需要提示从哪里开始。
一个与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拦截器来实现这个功能。
下面是一个示例。首先定义一个自定义的拦截器。我调用的API在达到速率限制时会响应HTTP Code 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();
}
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);
}
}
}
也许这可以帮到你。