美文网首页
OkHttp源码分析小结

OkHttp源码分析小结

作者: Dane_404 | 来源:发表于2019-03-19 23:17 被阅读0次

1、从newCall开始:

@Override 
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

实际上是RealCall,看它的newRealCall:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);  //返回的是EventListener.NONE
    return call;
}

2、接着我们会调用enqueue,看RealCall的enqueue:

@Override 
public void enqueue(Callback responseCallback) {
   synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");  //已经执行的抛出异常
      executed = true;
    }
   captureCallStackTrace();  //方法名意思是,捕捉请求的堆栈跟踪,会保存在retryAndFollowUpInterceptor中
   eventListener.callStart(this);  //EventListener.NONE默认没有处理任何方法,我理解是用户在Builder Ok时候设置用的
   client.dispatcher().enqueue(new AsyncCall(responseCallback));  //实际是执行Dispatcher的enqueue
}

接着看Dispatcher.enqueue:

synchronized void enqueue(AsyncCall call) {
    //正在运行的异步任务小于最大请求数64且此时同一个host的请求不超过5
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);  //添加到正在执行的任务列表
      executorService().execute(call);  //执行,交给线程池
    } else {
      readyAsyncCalls.add(call);  //否则,加入准备执行列表
    }
}

由于AsyncCall实际上是Runnable,它继承了NamedRunnable,NamedRunnable的run方法执行了execute,所以直接看AsyncCall的execute:

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain();  //关键
    if (retryAndFollowUpInterceptor.isCanceled()) {
      signalledCallback = true;
      responseCallback.onFailure(RealCall.this, new IOException("Canceled"));  //responseCallback我们传入的回调
    } 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);  //主要在运行队列移除这个任务
  }
}
}

getResponseWithInterceptorChain():

 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));  //真正访问网络
   //RealInterceptorChain的作用是把这些拦截器串起来
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
 }

看RealInterceptorChain的procee:

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
  RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();  //index 表示第几个拦截器

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
         + " must call proceed() exactly once");
   }

    // Call the next interceptor in the chain.  
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
       connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
       writeTimeout);    //又new了一个RealInterceptorChain ,但注意index + 1
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);  //把RealInterceptorChain 传入,里面又会调用process方法,直到所有拦截器走完

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

   // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
 }

3、下面一步一步看拦截器的执行,先看RetryAndFollowUpInterceptor,因为它是第一个:

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();
   //StreamAllocation,文档解释是协调Connections、Streams、Calls三者的关系
    streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
        call, eventListener, callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {  //循环,所以RetryAndFollowUpInterceptor最为第一个拦截器,其他拦截器都在这里这行,起到重新请求重定向的作用
    if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
    }

    Response response;
    boolean releaseConnection = true;
    try {
        response = realChain.proceed(request, streamAllocation, null, null);  //又调了proceed,执行下一个
        releaseConnection = false;
    } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), false, request)) {
        throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
    } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;
    } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
        streamAllocation.streamFailed(null);
        streamAllocation.release();
        }
    }

    // Attach the prior response if it exists. Such responses never have a body.
    if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
    }

    Request followUp = followUpRequest(response);

    if (followUp == null) {
        if (!forWebSocket) {
        streamAllocation.release();
        }
        return response;
    }

    closeQuietly(response.body());

    if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
    }

    if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
    }

    if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
    } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
    }

    request = followUp;
    priorResponse = response;
    }
}

看上面应该就明白了,而BridgeInterceptor主要是就请求和响应添加默认的请求头之类的信息,CacheInterceptor查找有没缓存,没有就交给下一个拦截器,也就是ConnectInterceptor:

@Override public Response intercept(Chain chain) throws IOException {
   RealInterceptorChain realChain = (RealInterceptorChain) chain;
   Request request = realChain.request();
   StreamAllocation streamAllocation = realChain.streamAllocation();

   // We need the network to satisfy this request. Possibly for validating a conditional GET.
   boolean doExtensiveHealthChecks = !request.method().equals("GET");
   //创建连接,连接池有的就拿连接池的
   HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
   RealConnection connection = streamAllocation.connection();  

   return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

最后一个拦截器,CallServerInterceptor:

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());  
    httpCodec.writeRequestHeaders(request);  //写入请求头
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
    // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
    // Continue" response before transmitting the request body. If we don't get that, return
    // what we did get (such as a 4xx response) without ever transmitting the request body.
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
    }

    if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        realChain.eventListener().requestBodyStart(realChain.call()); //写入请求体
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
    } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
        // from being reused. Otherwise we're still obligated to transmit the request body to
        // leave the connection in a consistent state.
        streamAllocation.noNewStreams();
    }
    }

    httpCodec.finishRequest();

    if (responseBuilder == null) {
    realChain.eventListener().responseHeadersStart(realChain.call());
    responseBuilder = httpCodec.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

    int code = response.code();
    if (forWebSocket && code == 101) {
    // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
    response = response.newBuilder()
        .body(Util.EMPTY_RESPONSE)
        .build();
    } else {
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
    streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
}

到这里,流程走完,小结下:OkHttp请求实际上通过各种拦截器串联执行过程,重新请求和重定向做为第一个拦截器,其他拦截器都在他的死循环里面执行,没有出现访问失败或异常就会跳出循环,有缓存时就会从缓存拿,不会再去访问网络,没缓存时,先打开连接,寻找连接池有没连接复用,没有在创建新的连接,然后请求网络。
4、连接池
当然大量的连接每次连接关闭都要三次握手四次分手的很显然会造成性能低下,因此http有一种叫做keepalive connections的机制,它可以在传输数据后仍然保持连接,当客户端需要再次获取数据时,直接使用刚刚空闲下来的连接而不需要再次握手。
来看看这个ConnectionPool的put,它会在ConnectInterceptor中创建新的连接时被调用:

void put(RealConnection connection) {
   assert (Thread.holdsLock(this));
   if (!cleanupRunning) {
     cleanupRunning = true;
     executor.execute(cleanupRunnable);   //这里开始执行清除操作,会定时执行
   }  
   connections.add(connection);  //添加到双向列表
 }

看cleanupRunnable,它是个Runnable:

private final Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
  while (true) {
    long waitNanos = cleanup(System.nanoTime());
    if (waitNanos == -1) return;
    if (waitNanos > 0) {
      long waitMillis = waitNanos / 1000000L;
      waitNanos -= (waitMillis * 1000000L);
      synchronized (ConnectionPool.this) {
        try {
          ConnectionPool.this.wait(waitMillis, (int) waitNanos);
        } catch (InterruptedException ignored) {
        }
      }
    }
  }
}
};

看cleanup方法:

long cleanup(long now) {
    int inUseConnectionCount = 0;
    int idleConnectionCount = 0;
    RealConnection longestIdleConnection = null;
    long longestIdleDurationNs = Long.MIN_VALUE;

    // Find either a connection to evict, or the time that the next eviction is due.
    synchronized (this) {
    for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        RealConnection connection = i.next();
        //查询此连接的StreamAllocation的引用数量,如果大于0则inUseConnectionCount数量加1,否则idleConnectionCount加1
        // If the connection is in use, keep searching.
        if (pruneAndGetAllocationCount(connection, now) > 0) {
        inUseConnectionCount++;
        continue;
        }

        idleConnectionCount++;

        // If the connection is ready to be evicted, we're done.
        long idleDurationNs = now - connection.idleAtNanos;
        if (idleDurationNs > longestIdleDurationNs) {
        longestIdleDurationNs = idleDurationNs;
        longestIdleConnection = connection;
        }
    }
     //如果空闲连接keepAlive时间超过5分钟,或者空闲连接数超过5个,则从Deque中移除此连接
    if (longestIdleDurationNs >= this.keepAliveDurationNs
        || idleConnectionCount > this.maxIdleConnections) {
        // We've found a connection to evict. Remove it from the list, then close it below (outside
        // of the synchronized block).
        connections.remove(longestIdleConnection);
    } else if (idleConnectionCount > 0) {  //如果空闲连接大于0,则返回此连接即将到期的时间
        // A connection will be ready to evict soon.
        return keepAliveDurationNs - longestIdleDurationNs;
    } else if (inUseConnectionCount > 0) {  //如果没有空闲连接,并且活跃连接大于0则返回5分钟
        // All connections are in use. It'll be at least the keep alive duration 'til we run again.
        return keepAliveDurationNs;
    } else {   //如果没有任何连接则跳出循环  
        // No connections, idle or in use.
        cleanupRunning = false;
        return -1;
    }
    }

    closeQuietly(longestIdleConnection.socket());

    // Cleanup again immediately.
    return 0;
}

cleanup所做的简单总结就是根据连接中的引用计数来计算空闲连接数和活跃连接数,然后标记出空闲的连接,如果空闲连接keepAlive时间超过5分钟,或者空闲连接数超过5个,则从Deque中移除此连接。接下来根据空闲连接或者活跃连接来返回下次需要清理的时间数:如果空闲连接大于0则返回此连接即将到期的时间,如果都是活跃连接并且大于0则返回默认的keepAlive时间5分钟,如果没有任何连接则跳出循环并返回-1。

相关文章

网友评论

      本文标题:OkHttp源码分析小结

      本文链接:https://www.haomeiwen.com/subject/jwoomqtx.html