Android - 剖析OKHttp(6)- 拦截器ConnectInterceptor

138 阅读4分钟

源码分析基于 3.14.4

ConnectInterceptor作用

主要负责建立或者复用Socket连接;

//ConnectInterceptor类中
  @Override public Response intercept(Chain chain) throws IOException {
    ......
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//1
    return realChain.proceed(request, transmitter, exchange);//2
  }
  • 注释1:主要逻辑都在transmitter.newExchange里面;
  • 注释2:调用下一个拦截器;
//Transmitter类中
 Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    ......
    ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);//调用下面的函数,查找一个可以使用的Connection
    ......
  }
  
//ExchangeFinder类中  
public ExchangeCodec find(
     ......
     RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);//最终会调到ExchangeFinder.findConnection
     ......
  }
//ExchangeFinder类中
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
      .......
      if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {//1
          foundPooledConnection = true;
          result = transmitter.connection;
        }
       .......
       if (result != null) {//找到可以复用的连接,则返回
           return result;
       }
        ......
        result = new RealConnection(connectionPool, selectedRoute);//2
        connectingConnection = result;
        ......
        result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);//3
        ......
        return result;
  }
  • 注释1: 从连接池里查找可复用的Connection,查找条件是:HTTP版本、支持加密版本、TLS版本、代理、SSL工厂、端口、Host等,找到了直接返回;
  • 连接池默认保存5个空闲连接,超时时间5分钟,如果5分钟内没有任何连接,则断开连接;
  • 注释2:如果没有找到,则构建一个RealConnection;
  • 注释3:result.connect,建立TCP+TLS连接;内部调用RealConnection.connectSocket建立Socket连接,也就是TCP;内部调用RealConnection.establishProtocol建立TLS连接,也就是HTTPS;

RealConnection.connect建立连接

//RealConnection类中
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
      EventListener eventListener) {
    .......
    while (true) {
      try {
        if (route.requiresTunnel()) {//1
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
          ......
        } else {//2
          connectSocket(connectTimeout, readTimeout, call, eventListener);
        }
        ......
  }
  • 注释1:表示需要用到隧道,通常是在Http代理连接上发送https请求;
//普通连接的请求行
GET /user HTTP/1.1 
//使用HTTP代理发送HTTPS请求
GET CONNECT www.baidu.com  HTTP/1.1
  • 如果使用http代理发送https请求,建立Socket连接以后发出CONNECT ,成功了就表示可以正常发送请求,后续的请求行就跟普通的一样;
  • 注释2:如果不使用隧道则connectSocket,建立Socket连接;

RealConnection.connectSocket建立Socket连接

  private void connectSocket(int connectTimeout, int readTimeout, Call call,
      EventListener eventListener) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket()//1
        : new Socket(proxy);//2
    ......
    Platform.get().connectSocket(rawSocket, route.socketAddress(), 
    connectTimeout);//3
 ......
  }
  • 注释1:创建普通连接;
  • 注释2:创建代理的连接new Socket(proxy);
  • 注释3:建立Socket连接;

连接池

public final class ConnectionPool {
  final RealConnectionPool delegate;
  public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);//1
  }
  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.delegate = new RealConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit);
  }
  • 空闲连接数大于5个或空闲时长大于5分钟,则清理; 1.缓存连接
//RealConnectionPool类中
  void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
      cleanupRunning = true;
      executor.execute(cleanupRunnable);//1
    }
    connections.add(connection);//2
  }
  • 注释1:启动cleanupRunnable线程开始清理空闲连接;
  • 注释2:缓存连接;
  1. 清理空闲连接
 //RealConnectionPool类中
  private final Runnable cleanupRunnable = () -> {
    while (true) {
      long waitNanos = cleanup(System.nanoTime());//调用下面的cleanup函数
      if (waitNanos == -1) return;//7
      if (waitNanos > 0) {
        long waitMillis = waitNanos / 1000000L;
        waitNanos -= (waitMillis * 1000000L);
        synchronized (RealConnectionPool.this) {
          try {
            RealConnectionPool.this.wait(waitMillis, (int) waitNanos);//8
          } catch (InterruptedException ignored) {
          }
        }
      }
    }
  };
 
//RealConnectionPool类中
long cleanup(long now) {
    synchronized (this) {
      for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {//1
        RealConnection connection = i.next();
        if (pruneAndGetAllocationCount(connection, now) > 0) {//判断连接是否使用,每个连接都有一个弱引用计数,当计数大于0,则表示在使用中
          inUseConnectionCount++;
          continue;
        }

        idleConnectionCount++;//空闲连接计数

        long idleDurationNs = now - connection.idleAtNanos;//计算空闲了多久
        if (idleDurationNs > longestIdleDurationNs) {//记录空闲最久的连接
          longestIdleDurationNs = idleDurationNs;
          longestIdleConnection = connection;
        }
      }

      if (longestIdleDurationNs >= this.keepAliveDurationNs
          || idleConnectionCount > this.maxIdleConnections) {//2
        connections.remove(longestIdleConnection);
      } else if (idleConnectionCount > 0) {//3
        return keepAliveDurationNs - longestIdleDurationNs;
      } else if (inUseConnectionCount > 0) {//4
        return keepAliveDurationNs;
      } else {
        cleanupRunning = false;
        return -1;//5
      }
    }
    ```
    closeQuietly(longestIdleConnection.socket());//6
    return 0;//9
  }
  • 注释1:遍历的目的,统计空闲连接数以及查找空闲最长的连接;
  • 注释2:当空闲连接闲置时间等于5分钟,则从集合移除;当空闲连接数大于5个,也从集合移除;
  • 注释3:当有空闲连接且注释2两个条件不满足,则返回等待时间(keepAliveDurationNs - longestIdleDurationNs),再开始清理,对应注释8;
  • 注释4:当没有空闲连接且注释2两个条件不满足,则返回等待时间5分钟,再开始清理;
  • 注释5:返回-1,表示即没有使用也没有空闲的,退出清理,对应注释7;
  • 注释6:关闭从集合移除的连接;
  • 注释9:返回0,不wait,继续查找;

总结

  • ConnectInterceptor连接拦截器,主要负责建立Socket连接,其实Okttp本身用的就是Socket;
  • 如果连接池中有可以使用的,则复用,复用的条件是:HTTP版本、支持加密版本、TLS版本、代理、SSL工厂、端口、Host等;
  • 连接池清理由cleanupRunnable 任务实现;

以上分析有不对的地方,请指出,互相学习,谢谢哦!