OkHttp - Interceptors(三)
本文中原始碼基於OkHttp 3.6.0
- 《OkHttp Request 請求執行流程》
- 《OkHttp - Interceptors(一)》
- 《OkHttp - Interceptors(二)》
- 《OkHttp - Interceptors(三)》
- 《OkHttp - Interceptors(四)》
這篇文章主要分析 ConnectInterceptor ,它是 OkHttp 請求鏈上的倒數第二個節點,其主要任務就是建立與伺服器的連線。
- ConnectInterceptor
先來看其 intercept 方法。
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.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
這裡的程式碼比較少,建立了一個 HTTP 編解碼器HttpCodec
和一個連線物件RealConnection
,並將它們加入到請求鏈中,其中HttpCodec
主要用於在CallServerInterceptor
中向伺服器發起 Request 和解析 Response。構建這兩個物件的主要邏輯全部封裝在StreamAllocation
中,它在請求鏈的第一個節點RetryAndFollowupInterceptor
中建立的。
來看其 newStream() 方法。
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// 如果 connection 是一個新建立的,那麼不用進行健康性測試
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// 健康檢查主要是判斷 socket 是否關閉,輸入輸出流是否關閉等。
// 如果連線不健康,則斷開連線,並從 connectionPool 中移除。
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
通過 findHealthyConnection() 找到一條可用的連線。
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// 如果 connection 是一個新建立的,那麼不用進行健康性測試
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}
// 健康檢查主要是判斷 socket 是否關閉,輸入輸出流是否關閉等。
// 如果連線不健康,則斷開連線,並從 connectionPool 中移除。
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
繼續進入 findConnection()。
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");
// StreamAllocation 會保持了一個當前的連線,如果這個連線允許繼續使用,則直接複用這個連線
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// 從連線池中找到一個可以複用的連線
Internal.instance.get(connectionPool, address, this);
if (connection != null) {
return connection;
}
selectedRoute = route;
}
// 如果沒有合適的連線,那麼就建立一個新的
// 尋找一個合適的路由,如果沒有找到就丟擲異常,由 RetryAndFollowUpInterceptor 負責捕獲並重試
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
}
RealConnection result;
synchronized (connectionPool) {
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);
acquire(result);
if (canceled) throw new IOException("Canceled");
}
// 執行三次握手,建立與伺服器的連線
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// 把這個連線放進連線池,供以後複用
Internal.instance.put(connectionPool, result);
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
return result;
}
在執行 connect() 的時候,使用 Socket 建立連線,如果請求是 HTTPS,則使用 SSLSocket 進行包裝。
那麼,至此客戶端與伺服器之間的連線就已經建立成功了,如果 OkHttpClient 中設定了 networkInterceptor 的話,下面就該把任務交給它們處理了,這裡就可以看出 networkInterceptor 和 Interceptor 之間的區別,networkInterceptor 處理請求時已經建立好連線。如果沒有設定 networkInterceptor,下面就該 CallServerInterceptor 出場了。
相關文章
- OkHttp Interceptors(四)HTTP
- OkHttp3深入瞭解之InterceptorsHTTP
- 抽絲剝繭okhttp(五)Interceptors原理HTTP
- spring mvc interceptorsSpringMVC
- 原始碼分析三:OkHttp—CacheInterceptor原始碼HTTP
- 原始碼分析三:OkHttp—CallServerInterceptor原始碼HTTPServer
- 原始碼分析三:OkHttp—RetryAndFollowUpInterceptor原始碼HTTP
- 原始碼分析三:OkHttp—BridgeInterceptor原始碼HTTP
- 原始碼分析三:OkHttp—ConnectInterceptor原始碼HTTP
- 抽絲剝繭okhttp(三)Response部分HTTP
- C# 12 攔截器 InterceptorsC#
- OkHttp 原始碼剖析系列(三)——快取機制HTTP原始碼快取
- 原始碼分析三:OkHttp(1)—總體架構原始碼HTTP架構
- OkHttp 3.7原始碼分析(三)——任務佇列HTTP原始碼佇列
- okhttpHTTP
- 原始碼分析三:OkHttp(2)—攔截器簡介原始碼HTTP
- OkHttp3.7原始碼分析(三)——任務佇列HTTP原始碼佇列
- 徹底理解OkHttp - OkHttp 原始碼解析及OkHttp的設計思想HTTP原始碼
- OkHttp3原始碼解析(三)——連線池複用HTTP原始碼
- OkHttp解析HTTP
- The content of element type "package" must match "(result-types?,interceptors?...Package
- 手撕OkHttpHTTP
- OkHttp的概述HTTP
- OkHttp優點HTTP
- okhttp 核心剖析HTTP
- OkHttp3HTTP
- OkHttp使用教程HTTP
- [jaeger] 三、實現一個分散式呼叫(OkHttp+SpringBoot)分散式HTTPSpring Boot
- OkHttp 知識梳理(3) OkHttp 之快取基礎HTTP快取
- axios的全域性攔截之axios.interceptorsiOS
- OkHttp 知識梳理(4) - OkHttp 之快取原始碼解析HTTP快取原始碼
- OkHttp 知識梳理(1) OkHttp 原始碼解析之入門HTTP原始碼
- OKHttp 官方文件【二】HTTP
- OKHttp 官方文件【一】HTTP
- OkHttp簡單剖析HTTP
- okhttp與nettyHTTPNetty
- OKHttp原始碼解析HTTP原始碼
- OkHttp原始碼分析HTTP原始碼