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에 따라 라이센스가 부여됩니다.