深入okhttp(责任链与生产者消费者模式)

434 阅读4分钟

okhttp流程

 okhttp由okhttpclient开始,call的实现为realcall,通过request发送的请求以及realcall共同实现网络请求,现在我们一起来看下realcall以及其下的重点

realcall

realcall有两步最重要的,分别是呼叫disptcher.enqueue()以及getresponsewithInterceptorChain()两个方法

 @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

然后我们可以先深入disptcher看看

dispatcher详解

private @Nullable ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

首先从成员变量来看,他维护了一个线程池,三个队列。

  • readyAsyncCalls队列是用于正在准备中的call

  • runningAsyncCalls队列用于放入线程池中执行的call

  • runningSyncCalls队列用于同步的call 这三个队列都是arraydeque(下节再讲,可以极大的提升数组插入的效率

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

异步执行便会调用enqueue()方法,这时会将该call放入线程池中执行同时加入runningAsyncCalls队列中。 而我们再看一下这个call是什么(AsyncCall)

 final class AsyncCall extends NamedRunnable

可以看到AsyncCall其实就是runnable,所以我们传进去的就是一个由线程所持有的call。而一边制造线程,一遍消耗,这种模式就是生产者消费者模式

拦截器

拦截器运用到的是责任链模式,拦截器的启动在于这个方法

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    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);//从RealCall的Request开始链式处理
  }

分层设计

在真实的网路中,分为osi七层模型,这样层与层之间相互依赖,降低了开发和维护的难度。
okhttp爷采用分层设计的思想,这种从网络层到应用层很适合用责任链模式来实现,每一层interceptor输入都是Request,输出都是response,然后可以一层层的向下传递,每层取出自己要用的数据

而okhttp拦截器的层次如下:

  • 自定义应用拦截器
  • 重试、重定向拦截器
  • 应用/网络桥接拦截器
  • 缓存拦截器
  • 连接拦截器
  • 自定义网络拦截器
  • 在线网络请求拦截器

开发者可以添加两种自定义Interceptor,一种是client.interceptors()应用层拦截器,一种是client.networkInterceptors()网络层拦截器。 但其实这两种都是Interceptor,为什么可以分成是应用层和网络层呢?

因为在网络层拦截器上方,是ConnectionInterceptor连接拦截器,这个拦截器里会提供Address、ConnectionPool等资源,可以用于处理网络连接,networkInterceptors是添加在这之后的,可以参与真正的网络层数据的处理。

重试、重定向拦截器

OkHttp正是把这个拦截器作为真正的入口,创建StreamAllocation对象,在StreamAllocation对象中准备了网络连接的Address、连接池等资源,后续的拦截器,使用的都是这个StreamAllocation对象。

网络环境本质上是不稳定的,已建立的连接可能突然不可用,或者连接可用但是服务器报错,这就需要重试/重定向功能,这也是RetryAndFollowUpInterceptor拦截器的分层功能。

应用转网络的桥接功能

BridgeInterceptor是个桥梁,这主要是指他会自动处理一些网络层特有的Header信息,例如Host属性,是HTTP1.1必须的,但应用层并不关心这个属性,这就是由BridgeInterceptor自动处理的。

缓存拦截器

在网络请求中使用缓存是非常必要提速手段,OkHttp专门用了CacheInterceptor拦截器来处理这个功能。 缓存的使用注意包括存储、查询和有效性检查,在OkHttp中:
存储,使用client外观模式来设置存储Cache数据的InternalCache实现类,在走请求链获取Response时记录cache。
查询,在存储Cache数据的InternalCache实现类中,根据Request过滤,来查找Cache。 有效性检查,利用工具类CacheStrategy的getCandidate函数,来判断Cache数据的各项指标是否达到条件。

连接拦截器

在RetryAndFollowUpInterceptor入口处,我们已经分析过,在OkHttp中,连接功能由StreamAlloc实现,提供Address地址、Route路由、RealConnection连接、ConnectionPool线程池复用、身份验证、协议、握手、平台、安全等功能。

在ConnectionInterceptor这一层,其实还没有真正连接网络,它的具体功能很简单,就是准备好request请求、streamAllocation连接资源、httpCodec传输工具、connection连接,为最底层的网络连接服务。

自定义网络拦截器

自定义的网络层拦截器相比应用层拦截器,能直接监测到在线网络请求的数据交换过程。 例如,Http有url重定向机制,如果Http返回码为301,就需要根据Header中Location字段的新url,重新发起一次请求,这样的话,总共会有两次请求。

在应用层的拦截器看来,第一次请求并没有返回有效数据,它只会抓到一次请求,也就是第二次的请求。 但是在网络层的拦截器看来,两次都是网络请求,所以它会抓到两次请求。

网络请求功能拦截器

前面所有的拦截器,都是在准备或处理网络连接前后的数据,只有CallServerInterceptor这个拦截器,是真正连接在线服务的。

它使用ConnectionInterceptor提供的HttpCodec传输工具来发出Request,获取Response,然后用ResponseBuilder生成最终的Response,再层层传递给外层的拦截器。