okhttp3 http请求响应是怎么发生的

1.在源码初探时,我们知道,okhttp3的请求响应在getResponseWithInterceptorChain()方法里面,今天我们就看看这里面做了什么。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    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));
<pre><code>**Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,**
    originalRequest, this, client.connectTimeoutMillis(),
    client.readTimeoutMillis(), client.writeTimeoutMillis());

boolean calledNoMoreExchanges = false;
try {
  **Response response = chain.proceed(originalRequest);**
  if (transmitter.isCanceled()) {
    closeQuietly(response);
    throw new IOException("Canceled");
  }
  return response;
} catch (IOException e) {
  calledNoMoreExchanges = true;
  throw transmitter.noMoreExchanges(e);
} finally {
  if (!calledNoMoreExchanges) {
    transmitter.noMoreExchanges(null);
  }
}

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange) throws IOException { if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null &amp;&amp; !this.exchange.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.exchange != null &amp;&amp; calls &gt; 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, transmitter, exchange,
    index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null &amp;&amp; index + 1 &lt; interceptors.size() &amp;&amp; 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;

}

典型的责任链模式,对请求和响应分别进行包装,递归调用proceed,calls++取出不同的拦截器

,此刻我们要我们应该就清楚了,各个拦截器实现了http的底层实现,我们只需要弄清楚这写拦截器都是干嘛用的就行了

1.client.interceptors(),这个是用户可以配置的,用来我们拿到最原始的处理做一些统一处理,同样也可以,做响应的过滤拦截

2.RetryAndFollowUpInterceptor

@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();</p>
<pre><code>int followUpCount = 0;
Response priorResponse = null;
**while (true) {**
  transmitter.prepareToConnect(request);

  if (transmitter.isCanceled()) {
    throw new IOException("Canceled");
  }
  //上面几行就是 检查request是不是被取消了,如果取消就退出循环,不在执行责任链上其他对象 

  Response response;
  boolean success = false;
  try {
    response = realChain.proceed(request, transmitter, null);
    success = true;
  } catch (RouteException e) {
    // The attempt to connect via a route failed. The request will not have been sent.
    if (!recover(e.getLastConnectException(), transmitter, false, request)) {
      throw e.getFirstConnectException();
    }
    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, transmitter, requestSendStarted, request)) throw e;
    continue;
  } finally {
    // The network call threw an exception. Release any resources.
    if (!success) {
      transmitter.exchangeDoneDueToException();
    }
  }

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

  Exchange exchange = Internal.instance.exchange(response);
  Route route = exchange != null ? exchange.connection().route() : null;
  Request followUp = followUpRequest(response, route);

  if (followUp == null) {
    if (exchange != null &amp;&amp; exchange.isDuplex()) {
      transmitter.timeoutEarlyExit();
    }
    return response;
  }

  RequestBody followUpBody = followUp.body();
  if (followUpBody != null &amp;&amp; followUpBody.isOneShot()) {
    return response;
  }

  closeQuietly(response.body());
  if (transmitter.hasExchange()) {
    exchange.detachWithViolence();
  }

  if (++followUpCount &gt; MAX_FOLLOW_UPS) {
    throw new ProtocolException("Too many follow-up requests: " + followUpCount);
  }

  request = followUp;
  priorResponse = response;
}

}

看了一眼有点难,大概说说做了什么,在请求前初始化了一个ExchangeFinder存入了transmitter

RetryAndFollowUpInterceptor:做请求重连,重定向等操作

通过循环遍历,不断请求,修正请求,直到请求成功返回

3.BridgeInterceptor,这个简单一点,主要是初始化request中的header,其中有点要注意,若没配置Accept-Encoding,默认给gzip压缩数据,收到response后也会用gzip对数据进行解压缩还给上一个Interceptor

4.CacheInterceptor 做缓存拦截,如果有这个轻求的响应直接返回数据,没有的话,把请求封装后给下一个处理器,收到数据后,将请求响应缓存起来。

5.ConnectInterceptor这个是最难的,exchangeFinder在RetryAndFollowUpInterceptor初始过了,在这有用到,初始化resultConnection,里面一道findConnection函数

1.直接用上次的 ,在重试中没有清理的

releasedConnection = transmitter.connection;
toClose = transmitter.connection != null && transmitter.connection.noNewExchanges
? transmitter.releaseConnectionNoEvents()
: null;</p>
<p>if(transmitter.connection != null) {
// We had an already-allocated connection and it's good.
result = transmitter.connection;
releasedConnection = null;
}

2.去连接池去拿,同一个host,不同访问接口

if(result == null) {
// Attempt to get a connection from the pool.
if(connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
foundPooledConnection = true;
result = transmitter.connection;
}else if(nextRouteToTry != null) {
selectedRoute = nextRouteToTry;
nextRouteToTry = null;
}else if(retryCurrentRoute()) {
selectedRoute = transmitter.connection.route();
}
}

3.去连接池中拿可以共享的,多路复用

if(newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
routes = routeSelection.getAll();
if(connectionPool.transmitterAcquirePooledConnection(
address, transmitter, routes, false)) {
foundPooledConnection = true;
result = transmitter.connection;
}
}

4.自己创建,先不用

// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
connectionPool.routeDatabase.connected(result.route());

5.再去连接池里面去拿一次

Socket socket = null;
synchronized(connectionPool) {
connectingConnection = null;
// Last attempt at connection coalescing, which only occurs if we attempted multiple
// concurrent connections to the same host.
if(connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
// We lost the race! Close the connection we created and return the pooled connection.
result.noNewExchanges = true;
socket = result.socket();
result = transmitter.connection;</p>
<pre><code>// It's possible for us to obtain a coalesced connection that is immediately unhealthy. In
// that case we will retry the route we just successfully connected with.
nextRouteToTry = selectedRoute;

}else{ connectionPool.put(result); transmitter.acquireConnectionNoEvents(result); } }

6.没办了,只能用自己的了。