OKHttp 原理

OkHttp是一个高性能的网络库,支持HTTPS/HTTP1.1/HTTP2/WebSocket等底层协议,具备连接池、连接复用、缓存控制等功能。采用建造者、工厂、享元、责任链等设计模式,实现Request和Response的分层处理。同步和异步请求通过Dispatcher调度,利用拦截器链处理请求和响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OkHttp 是一个高性能的网络库,主要功能如下:

  1. 支持底层协议(HTTPS / HTTP1.1 / HTTP2 / WebSocket),包括连接池、连接复用、缓存控制等
  2. 维护任务队列线程池,支持友好并发访问
  3. 提供拦截器链,实现 Request 和 Response 分层处理

OkHttp 中涉及到的设计模式

  • 建造者模式。构建 OkHttpClient
  • 工厂模式。OkHttpClient 实现了 Call.Factory,用于生产 Call 产品(RealCall)
  • 享元模式。通过线程池、连接池共享对象
  • 责任链模式。将不同功能的拦截器形成一个链

同步请求

RealCall 表示被执行的请求,调用 execute 方法执行同步请求,最终会把 RealCall 放到 Dispatcher 的同步双端队列中顺序执行,执行结束后调用 finish 方法,把 RealCall 请求从 Dispatcher 中移除,无论是同步还是异步请求,最终都会调用 getResponseWithInterceptorChain 方法,在 OkHttp 中,拦截器链会依次完成相应功能

Call 对象只能执行一次,否则会抛出异常

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().executed(this);
    return getResponseWithInterceptorChain();
  }

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

异步请求

AsyncCall 表示被执行的请求,调用 enqueue 方法执行异步请求,如果当前所有请求数(64)和单一 host 请求数(5)满足要求,则把 AsyncCall 放到 Dispatcher 的 Running 队列中顺序执行,否则放到 waiting 队列中,AsyncCall 执行结束后调用 finish 方法,把当前 AsyncCall 从 running 队列中移除并执行线程调度

 @Override public void enqueue(Callback responseCallback) {
   synchronized (this) {
     if (executed) throw new IllegalStateException("Already Executed");
     executed = true;
   }
   client.dispatcher().enqueue(new AsyncCall(responseCallback));
 }

 synchronized void enqueue(AsyncCall call) {
   if (runningAsyncCalls.size() < maxRequests
              && runningCallsForHost(call) < maxRequestsPerHost) {
     runningAsyncCalls.add(call);
     executorService().execute(call);
   } else {
     readyAsyncCalls.add(call);
   }
 }

  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    @Override protected void execute() {
      Response response = getResponseWithInterceptorChain();
      if (retryAndFollowUpInterceptor.isCanceled()) {
        responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
      } else {
        responseCallback.onResponse(RealCall.this, response);
      }
    }
  }

Dispatcher 调度器:维护请求状态、维护线程池(减少创建线程的开销、减少线程过少造成 CPU 闲置、减少线程过多时内存和线程切换的开销)

  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    synchronized (this) {
      calls.remove(call)
      if (promoteCalls) promoteCalls();
    }
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return;
    if (readyAsyncCalls.isEmpty()) return;
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext();) {
      AsyncCall call = i.next();
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
      if (runningAsyncCalls.size() >= maxRequests) return;
    }
  }

拦截器链

逻辑大致分为两部分:

  1. 创建一系列拦截器,并将其放入拦截器数组中。这部分拦截器即包括用户自定义的拦截器也包括框架内置的拦截器
  2. 创建一个拦截器链 RealInterceptorChain,并执行拦截器链的 proceed 方法
Response getResponseWithInterceptorChain() throws IOException {
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
}

public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  private final int index;
  private final Request request;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors,
                StreamAllocation streamAllocation, HttpCodec httpCodec,
                RealConnection connection, int index, Request request) {

  public Response proceed(Request request,
                  StreamAllocation streamAllocation, HttpCodec httpCodec,
                  RealConnection connection) throws IOException {

    ...

    RealInterceptorChain next = 
                  new RealInterceptorChain(interceptors, streamAllocation,
                  httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    
    ...

    return response;
  }
}

Chain.proceed 方法做了两件事

  • 创建下一个拦截链。传入 index + 1 使得下一个拦截器链只能从下一个拦截器开始访问
  • 执行索引为 index 的 intercept 方法,并将下一个拦截器链传入该方法

拦截器的 intercept 方法所执行的逻辑大致分为三部分

  • 在发起请求前对 request 进行处理
  • 调用下一个拦截器,获取 response
  • 对 response 进行处理,返回给上一个拦截器

Okhttp 中除了用户自定义的拦截器外还内置了若干核心拦截器,具体功能如下

RetryAndFollowUpInterceptor

  • 在网络请求失败后进行重试
  • 当服务器返回 3XX(重定向) 时直接发起新的请求,并在条件允许情况下复用当前连接

BridgeInterceptor

  • 设置内容长度,内容编码
  • 设置 GZIP 压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
  • 添加 Cookie
  • 设置其他 Header 数据,如 User-Agent、Host、Connection = Keep-alive 等

CacheInterceptor

  • 当网络请求有符合要求的 Cache 时直接返回 Cache
  • 当服务器返回内容有改变时更新当前 cache
  • 如果当前cache失效,删除

ConnectInterceptor

为当前请求找到合适的连接,可能复用已有连接也可能是重新创建的连接

CallServerInterceptor

负责向服务器发起真正的访问请求,并在接收到服务器返回后读取响应返回

HTTP2连接复用规则

RealConnection 描述一个物理 Socket 连接,连接池中维护多个 RealConnection 实例。由于HTTP2 支持多路复用,一个 RealConnection 可以支持多个网络访问请求,所以 OkHttp 又引入了 StreamAllocation 来描述一个实际的网络请求。通过 RealConnection 中的 StreamAllocation 弱引用队列是否为 0 来判断当前 RealConnection 是否空闲

  • 通过 ConnectionPool 来管理连接池,ConnectionPool 有一个独立的线程 cleanupRunnable 来清理连接池,其触发时机有两个:当连接池新增连接时、当连接闲置时(connectionBecameIdle 接口被调用时)
  • 搜集所有的空闲连接并记录 KeepAlive 时间,如果空闲连接超过 5 个或者某个连接的 KeepAlive 时间大于五分钟,就移除 KeepAlive 时间最长的连接,立刻再次扫描
  • 如果目前空闲连接不多于 5 个且 KeepAlive 时间都不大于 5 分钟,就返回最大 KeepAlive 的剩余的时间,供下次清理
  • 如果(全部都是活跃的连接),就返回默认的 keep-alive 时间,也就是 5 分钟后再执行清理
OkHttp 是一个开源的 HTTP 客户端,用于 Android 平台和 Java 应用。它建立在 Java 的 HttpURLConnection 类之上,并提供了更简洁、更强大的 API。 OkHttp 的工作原理主要涉及以下几个关键组件: 1. `OkHttpClient`:这是 OkHttp 的核心类,负责配置和创建请求、设置拦截器、管理连接池等。你可以通过构建 OkHttpClient 实例来自定义请求的行为和参数。 2. `Request`:表示一个 HTTP 请求,包括 URL、请求方法(如 GET、POST)、请求体、请求头等信息。你可以通过 Request.Builder 构建一个 Request 实例。 3. `Response`:表示一个 HTTP 响应,包括响应码、响应体、响应头等信息。OkHttp 会将服务器返回的数据解析成 Response 对象。 4. `Interceptor`:拦截器用于在发送请求和接收响应之前进行一些额外的处理。OkHttp 提供了很多内置的拦截器,如重试拦截器、缓存拦截器等,同时也支持自定义拦截器。 5. `Dispatcher`:调度器负责管理请求的调度和执行。它可以控制同时并发执行的请求数量,还可以设置请求超时时间等。 6. `ConnectionPool`:连接池用于管理 HTTP 连接的复用和回收。OkHttp 会自动复用连接以减少网络延迟,提高性能。 7. `Cache`:缓存可以保存服务器返回的响应,以便在后续的请求中复用。OkHttp 支持对响应进行缓存,并提供了灵活的配置选项。 当你使用 OkHttp 发起一个网络请求时,它会通过 OkHttpClient 来创建一个 Request 对象,并通过 Dispatcher 来执行这个请求。在执行过程中,OkHttp 会根据设置的拦截器进行一系列的处理,如添加请求头、重试、缓存等。最终,OkHttp 将返回一个 Response 对象,你可以从中获取到服务器返回的数据。 总体来说,OkHttp 的工作原理是通过封装底层的 HttpURLConnection,提供了简洁易用的 API,并通过拦截器和连接池等机制优化了网络请求的性能和可定制性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-sparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值