ConnectInterceptor 解析

2019-04-04  本文已影响0人  taijielan

ConnectInterceptor 这个类的主要作用是打开一个到指定的服务器的连接并且向下分发拦截。
构造函数中传入一个OkHttpClinet对象.

 public final OkHttpClient client;
  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

看下它的拦截过程,很简单

 @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.     //需要网络请求来满足这个请求,可能用get请求来验证。
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);// 1
    RealConnection connection = streamAllocation.connection();// 2

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
 // Drop the cached response if it's missing a required handshake.
      //并且本地缓存缺少必要的握手,将删除本地缓存,则返回不带本地缓存的策略
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }

下面看下streamAllocation.newStream(client, doExtensiveHealthChecks)方法,

  public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
    int connectTimeout = client.connectTimeoutMillis();
    int readTimeout = client.readTimeoutMillis();
    int writeTimeout = client.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
      //找到一个健康的连接
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }

得到一个可用的连接,在resultConnection.newCodec(client, this)中根据连接不同生成对应的http解析器对象

 public HttpCodec newCodec(
      OkHttpClient client, StreamAllocation streamAllocation) throws SocketException {
    if (http2Connection != null) {
      return new Http2Codec(client, streamAllocation, http2Connection);
    } else {
      socket.setSoTimeout(client.readTimeoutMillis());
      source.timeout().timeout(client.readTimeoutMillis(), MILLISECONDS);
      sink.timeout().timeout(client.writeTimeoutMillis(), MILLISECONDS);
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }

看下findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks)方法。

 /**
   * Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated
   * until a healthy connection is found.
   * 找到一个可用的连接,如果这个连接不可用,将会重新查找,直到找到到可用的连接
   */
  private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);
      // If this is a brand new connection, we can skip the extensive health checks.
      // 如果这个一个全新的连接,跳过全面的健康性检查
      synchronized (connectionPool) {
        if (candidate.successCount == 0) {
          return candidate;
        }
      }
      // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
      // isn't, take it out of the pool and start again.
      // 检查的目的是确认连接是否正常,如果不正常,从连接池中移除,并重新开始建立连接
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
        noNewStreams();
        continue;
      }

      return candidate;
    }
  }

里面的过程是

/**
   * Returns a connection to host a new stream. This prefers the existing connection if it exists,
   * then the pool, finally building a new connection.
   * 如果有一个创建好的连接,返回创建好的连接,如果没有这样的连接,那就在pool在中创建,如果还没有,构建一个新的连接。
   *
   */
  private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    synchronized (connectionPool) {
      if (released) throw new IllegalStateException("released");
      if (codec != null) throw new IllegalStateException("codec != null");
      if (canceled) throw new IOException("Canceled");

      // Attempt to use an already-allocated connection.
      // 1 试图使用一个已经配置好了的连接
      RealConnection allocatedConnection = this.connection;
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }
      // Attempt to get a connection from the pool.
      // 2 试图从pool中获取一个链接 ,具体的实现在okhttpclient中
      Internal.instance.get(connectionPool, address, this);
      if (connection != null) {
        return connection;
      }

      selectedRoute = route;
    }

    // If we need a route, make one. This is a blocking operation.
    // 如果需要一个路由,创建一个
    if (selectedRoute == null) {
      selectedRoute = routeSelector.next();
    }
    // 3 如果没有的话就创建一个连接
    // Create a connection and assign it to this allocation immediately. This makes it possible for
    // an asynchronous cancel() to interrupt the handshake we're about to do.
    RealConnection result;
    synchronized (connectionPool) {
      route = selectedRoute;
      refusedStreamCount = 0;
      result = new RealConnection(connectionPool, selectedRoute);
      acquire(result);
      if (canceled) throw new IOException("Canceled");
    }

    // Do TCP + TLS handshakes. This is a blocking operation. 建立握手 ,这是堵塞操作
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    //移除失败的记录
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      // Pool the connection.
      //将当前result 放到pool中并进行pool中的整理。
      Internal.instance.put(connectionPool, result);

      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one.
      //如果同时另一个多路复用连接到相同的地址上,那么释放掉这个连接,并获取另一个。
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    return result;
  }

寻找连接的过程:

public void connect(
      ...
    while (true) {
      try {
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout);
        } else {
          connectSocket(connectTimeout, readTimeout);
        }
        establishProtocol(connectionSpecSelector);
        break;
      } catch (IOException e) {
       ...
    }
  }

在这里也算是okhttp中真正的连接位置。

上一篇下一篇

猜你喜欢

热点阅读