OKHttp 3 소스 코드 분석 - Connect Interceptor
                                            
 67767 단어  Android오픈 소스 라 이브 러 리
                    
ConnectInterceptor, StreamAllocation, RealConnection, ConnectionPool 를 포함한다.우 리 는 먼저 분석
ConnectionPool 한 후에 분석 ConnectionInterceptor 했다.연결 탱크
ConnectionPool 역할 은 주로 재 활용 Http 연결 로 네트워크 연결 의 지연 을 피하 고 TCP 튜 닝 으로 인 한 대역 폭 이 너무 작은 문 제 를 피 하 는 것 이다.ConnectionPool 주로 두 가지 방법 이 있다.get 방법 은 연결 을 얻 는 데 사용 된다.put 방법 은 연결 을 추가 하 는 데 사용 된다.get 방법
public final class ConnectionPool {
    private final Deque<RealConnection> connections = new ArrayDeque<>();
	@Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
    	assert (Thread.holdsLock(this));
    	for (RealConnection connection : connections) {
      		if (connection.isEligible(address, route)) {
        		streamAllocation.acquire(connection, true);
        		return connection;
      		}
    	}
  		return null;
  }	    
}
  연결 탱크 의 연결 을 옮 겨 다 니 며
address 와 route 를 매개 변수 호출 RealConnection 대상 으로 하 는 방법 isEligible 으로 연결 이 적합 한 지 판단 합 니 다.적절 하 다 면
StreamAllocation 의 acquire 방법 을 호출 한 후 RealConnection 대상 으로 돌아간다.모든 연결 이 맞지 않 을 때 되 돌아 오기 null.StreamAllocation 의 acquire 방법 을 살 펴 보 자.public final class StreamAllocation {
    public void acquire(RealConnection connection, boolean reportedAcquired) {
        assert (Thread.holdsLock(connectionPool));
        if (this.connection != null) throw new IllegalStateException();
        this.connection = connection;
        this.reportedAcquired = reportedAcquired;
        connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
 	}
}
  StreamAllocation 의 변수 connection 가 null 인지 아 닌 지 를 먼저 판단 한다.null 가 아 닌 연결 이 포함 되 어 있 음 을 설명 하 며 상태 이상 을 직접 던 집 니 다.가장 중요 한 창설 대상
StreamAllocationReference 은 StreamAllocationReference 의 약 한 인용 이다.그리고 StreamAllocation 대상 RealConnection 의 connection 변수 에 추가 합 니 다.이렇게 하면 allocations 의 RealConnection 변 수 를 옮 겨 다 니 며 allocations 사용 중인 지 확인 할 수 있다.put 방법
public final class ConnectionPool {
    private final Deque<RealConnection> connections = new ArrayDeque<>();
	void put(RealConnection connection) {
        assert (Thread.holdsLock(this));
        if (!cleanupRunning) {
          cleanupRunning = true;
          executor.execute(cleanupRunnable);
        }
        connections.add(connection);
  	}
}
  StreamAllocation 방법 은 비교적 간단 하 다. 먼저 판단 put, cleanupRunning 은 연결 을 정리 하 는 작업 이 실행 중이 라 고 밝 혔 다.실행 되 지 않 으 면 스 레 드 탱크 를 호출 하여 실행 합 니 다 cleanupRunning.그 다음 에 cleanupRunnable 대상 RealConnection 을 변수 connection 에 추가 했다.연결 청소
public final class ConnectionPool {
    private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
        //    cleanup         。
        long waitNanos = cleanup(System.nanoTime());
        //     -1    
        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) {
            }
          }
        }
      }
    }
  };
}
  connections 방법 은 세 개의 값 을 되 돌려 줍 니 다. 하 나 는 - 1 로 직접 돌아 갑 니 다. 하 나 는 0 이 순환 을 실행 하고 계속 청 소 를 하 는 것 과 같 습 니 다. 0 이상 이면 청 소 를 기다 리 는 시간 을 설정 합 니 다.방법 을 분석 해 보 겠 습 니 다.
반환 값
cleanUp 현재 연결 되 지 않 았 음 cleanUp 계속 청소 작업 이 필요 하 다 고 밝 혔 다 -1 보다 크다 public final class ConnectionPool {
    long cleanup(long now) {
    	int inUseConnectionCount = 0;
    	int idleConnectionCount = 0;
    	RealConnection longestIdleConnection = null;
    	long longestIdleDurationNs = Long.MIN_VALUE;
        ......
    }
}
  먼저 변 수 를 정의 합 니 다.
0 는 사용 중인 연결 수량 을 나타 낸다.0 남 은 연결 수량 을 나타 낸다.inUseConnectionCount 은 여가 시간 이 가장 긴 연결 을 나타 낸다.idleConnectionCount 는 가장 긴 여가 시간 을 나타 낸다.public final class ConnectionPool {
    long cleanup(long now) {
        ......
        synchronized (this) {
      		for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        		RealConnection connection = i.next();
        		// 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;
        		}
      		}
             ......
    	}
    }
}
  순환
longestIdleConnection, 호출 longestIdleDurationNs 가 져 오기 connections 대상 의 pruneAndGetAllocationCount 수량 을 반복 합 니 다. RealConnection 보다 많 으 면 존재 StreamAllocation 가 사용 중 이 며 증가 0 합 니 다.RealConnection 로 설명 inUseConnectionCount 대상 이 이미 비어 있다 면 증가 0.그리고 연결 의 남 은 시간 을 계산 합 니 다.남 은 시간 이 현재 의 최대 치 RealConnection 보다 많 을 때, 우 리 는 값 idleConnectionCount 과 longestIdleDurationNs 을 부여 합 니 다.public final class ConnectionPool {    
    long cleanup(long now) {  
        ......
        synchronized (this) {
            ......
            //                                    ,
        	if (longestIdleDurationNs >= this.keepAliveDurationNs
          		|| idleConnectionCount > this.maxIdleConnections) {
                //    ,     。
        		connections.remove(longestIdleConnection);
      		} else if (idleConnectionCount > 0) {
                //       。
        		return keepAliveDurationNs - longestIdleDurationNs;
      		} else if (inUseConnectionCount > 0) {
                //       ,         
        		return keepAliveDurationNs;
      		} else {
                //      ,    -1。
        		cleanupRunning = false;
        		return -1;
      		}
    	}
        //           ,   0,    clean。
    	closeQuietly(longestIdleConnection.socket());
    	return 0;
  	}
}
  상술 한 코드 는 이미 주석 을 달 았 으 며, 서로 다른 조건 에 따라 응답 처 리 를 한다.
우 리 는 이어서
longestIdleDurationNs 방법의 논 리 를 분석 해 보 자.public final class ConnectionPool {    
	private int pruneAndGetAllocationCount(RealConnection connection, long now) {
        List<Reference<StreamAllocation>> references = connection.allocations;
        for (int i = 0; i < references.size(); ) {
          	Reference<StreamAllocation> reference = references.get(i);
          	if (reference.get() != null) {
            	i++;
            	continue;
          	}
          	references.remove(i);
          	connection.noNewStreams = true;
          // If this was the last allocation, the connection is eligible for immediate eviction.
          	if (references.isEmpty()) {
            	connection.idleAtNanos = now - keepAliveDurationNs;
            	return 0;
          	}
        }
        return references.size();
  	}
}
  longestIdleConnection 약 인용 pruneAndGetAllocationCount 대상.RealConnection 약 한 인용 이 존재 하 는 지 여 부 를 옮 겨 다 니 는 것 이다.존재 한다 면 StreamAllocation 사용 하고 있다 는 뜻 이 고, 말 이 없 으 면 이미 사용 하지 않 는 다 는 뜻 이다.if (references.isEmpty()) {
	connection.idleAtNanos = now - keepAliveDurationNs;
	return 0;
}
  이 코드 는 연결 의 생존 시간 을 단축 시 키 고 현재 시간 에서 생존 시간 을 빼 면 현재 시간 에 회수 할 수 있다 는 것 을 의미한다.생존 전략 과 충돌 합 니 다.
ConnectInterceptor
public final class ConnectInterceptor implements Interceptor {
	@Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        //  StreamAllocation newStream  
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        //  StreamAllocation RealConnection  
        RealConnection connection = streamAllocation.connection();
	    //        
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }    
}
  pruneAndGetAllocationCount 먼저 RealConnection 가 만 든 ConnectInterceptor 을 얻 었 습 니 다.이어서
RetryAndFollowUpInterceptor 대상 StreamAllocation 방법 으로 StreamAllocation 대상 을 얻 을 수 있 습 니 다.newStream 의 HttpCodec 방법.StreamAllocation 의 connection 방법 으로 처리 했다.다음 차단기 에 전달 하 는 것 이다.다음은 일일이 분석 하 겠 습 니 다.
Stream Allocation 의
Chain 방법proceed 의 newStream 방법 이 비교적 많 고 단계별 로 읽는다.public final class StreamAllocation {
	 public HttpCodec newStream(
      	OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    	int connectTimeout = chain.connectTimeoutMillis();
    	int readTimeout = chain.readTimeoutMillis();
    	int writeTimeout = chain.writeTimeoutMillis();
    	int pingIntervalMillis = client.pingIntervalMillis();
    	boolean connectionRetryEnabled = client.retryOnConnectionFailure();
    	......
}
  시간 초과 등 정 보 를 읽 습 니 다.
public final class StreamAllocation {
	 public HttpCodec newStream(
      	OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
        ......
        try {
          RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
              writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
          HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
          synchronized (connectionPool) {
            codec = resultCodec;
            return resultCodec;
          }
        } catch (IOException e) {
          throw new RouteException(e);
        }
     }
}
  주로 두 가지 기능 입 니 다.
StreamAllocation 방법 으로 연결 획득, newStream 의 findHealthyConnection 방법 으로 얻 은 후 되 돌려 준다.RealConnection 의 newCodec 방법 을 분석 하여 StreamAllocation 대상 을 얻 자.public final class StreamAllocation {
    private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      	int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
      	boolean doExtensiveHealthChecks) throws IOException {
        while (true) {
          RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
              pingIntervalMillis, connectionRetryEnabled);
          //successCount 0       ,           successCount
          synchronized (connectionPool) {
            if (candidate.successCount == 0) {
              return candidate;
            }
          }
          //           
          if (!candidate.isHealthy(doExtensiveHealthChecks)) {
            noNewStreams();
            continue;
          }
          return candidate;
        }
      }
}
  순환 호출
findHealthyConnection 방법 으로 대상 획득 RealConnection.그리고 findConnection 대상 을 판단 하 는 RealConnection 은 RealConnection 이 고 successCount 설명 0 이 사용 되 지 않 으 면 바로 돌아간다.0 를 위해 서 는 현재 RealConnection 의 건강 여 부 를 0 방법 으로 판단 해 야 하지 않 으 며, 주로 isHealthy 의 폐쇄 여부 와 흐름 의 폐쇄 여 부 를 판단 해 야 한다.RealConnection 의 Socket 방법 이 비교적 많 습 니 다. 우 리 는 관건 적 인 코드 만 봅 니 다.public final class StreamAllocation {
    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
       Connection releasedConnection;
       synchronized (connectionPool) {
           //    
       	   if (released) throw new IllegalStateException("released");
      	   if (codec != null) throw new IllegalStateException("codec != null");
      	   if (canceled) throw new IOException("Canceled");
           //    ,  
           if (result == null) {
               //             
               Internal.instance.get(connectionPool, address, this, null);
               if (connection != null) {
                 foundPooledConnection = true;
                 result = connection;
               } else {
                 selectedRoute = route;
               }
          }
       }
       if (result != null) {
           return result;
       }
	   ......
    }
}
  먼저 잘못 사용 한 다음 에
StreamAllocation 의 findConnection 방법 으로 연결 풀 Internal 에서 연결 을 얻 습 니 다.get 가 져 오지 않 으 면 connectionPool 바로 돌아 갑 니 다.public final class StreamAllocation {
    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
	synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");
	  //  Route           
      if (newRouteSelection) {
        List<Route> routes = routeSelection.getAll();
        for (int i = 0, size = routes.size(); i < size; i++) {
          Route route = routes.get(i);
          Internal.instance.get(connectionPool, address, this, route);
          if (connection != null) {
            foundPooledConnection = true;
            result = connection;
            this.route = route;
            break;
          }
        }
      }
     //            
     if (!foundPooledConnection) {
        if (selectedRoute == null) {
          selectedRoute = routeSelection.next();
        }
        route = selectedRoute;
        refusedStreamCount = 0;
        //  RealConnection  
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
      }
  	}
}
  result 에 따라 연결 풀 null 에서 대상 을 다시 가 져 옵 니 다.가 져 오지 못 하면 대상 을 만 듭 니 다 Route.public final class StreamAllocation {
    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
    if (foundPooledConnection) {
      return result;
    }
    .......
}
  연결 풀 에서 연결 대상 을 가 져 오 면
connectionPool 은 RealConnection 이 고 바로 돌아간다.public final class StreamAllocation {
    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
    if (foundPooledConnection) {
      return result;
    }
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());
    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;
      // Pool the connection.
      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);
    eventListener.connectionAcquired(call, result);
    return result;
}
  foundPooledConnection 는 true 연결 풀 에서 연결 대상 을 얻 지 못 했다 는 설명 이다. 그것 이 바로 새로 만 든 연결 이다.우 리 는 foundPooledConnection 의 false 를 호출 하여 연결 해 야 한다.그 다음 에 RealConnection 대상 을 호출 하 는 connect 방법 으로 연결 을 연결 풀 에 넣 고 마지막 으로 돌아 갑 니 다.이 어 분석
Internal 방법 put 이 뒤 를 이 었 다.public final class RealConnection extends Http2Connection.Listener implements Connection {
    public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
      StreamAllocation streamAllocation) throws SocketException {
        if (http2Connection != null) {
          return new Http2Codec(client, chain, streamAllocation, http2Connection);
        } else {
          socket.setSoTimeout(chain.readTimeoutMillis());
          source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
          sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
          return new Http1Codec(client, streamAllocation, source, sink);
        }
  }
}
  주로 창설
RealConnection.newCodec 인 코딩 요청, 디 코딩 응답 에 사 용 됩 니 다.StreamAllocation 의 connection 방법
public final class StreamAllocation{
    public synchronized RealConnection connection() {
    	return connection;
	}
}
  HttpCodec 의 HttpCodec 변 수 를 직접 되 돌려 줍 니 다.
                이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.