【问题标题】:OkHttp API rate limitOkHttp API 速率限制
【发布时间】:2016-02-12 14:18:41
【问题描述】:

OkHttp 是否有一种符合 api 速率请求限制的集成方式,还是必须在外部实现?无论哪种情况,都可以提示从哪里开始。

【问题讨论】:

    标签: okhttp rate rate-limiting


    【解决方案1】:

    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());
        }
    }
    

    【讨论】:

      【解决方案2】:

      正如@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();
          }
      

      【讨论】:

        【解决方案3】:

        您可以构建一个interceptor 来跟踪发出的请求,如果速率太高,可能会限制或失败请求。

        【讨论】:

        • 这是我不知道如何实现的节流部分,理想的情况是告诉客户端延迟队列,因为请求可以同时来自多个片段,但我找不到这样做的方法
        • Guava 中有一个速率限制器。您可以使用它,也可以研究它的源代码以开始使用。还可以考虑使用 OkHttp 的异步 API 来限制活动线程的数量。 Dispatcher 有配置选项。
        • 感谢您的提示,我会调查一下
        【解决方案4】:

        我也有这个问题。 通过邮寄上传大文件时,我想要限制速率。我阅读了 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);
            }
        }
        

        }

        也许这对你有帮助。

        【讨论】:

          猜你喜欢
          • 2020-11-04
          • 1970-01-01
          • 2014-08-05
          • 1970-01-01
          • 1970-01-01
          • 2015-08-13
          • 2015-03-24
          • 2015-04-30
          相关资源
          最近更新 更多