39. OkHttp之-攔截器-ConnectInterceptor

這個攔截器的職責很簡單,就是獲取一份和服務器的連接,具體來說就是一個socket,然後執行下一個攔截器。

他的功能主要是StreamAllocation這個類完成的,StreamAllocation是在第一個攔截器:重定向攔截器創建的。

    public HttpCodec newStream(
            OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
        ....
        try {
            //找到一個健康的連接
            RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                    writeTimeout, pingIntervalMillis, connectionRetryEnabled,
                    doExtensiveHealthChecks);
            //利用連接實例化流HttpCodec對象,如果是HTTP/2返回Http2Codec,否則返回Http1Codec
            HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

            synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
            }
        } catch (IOException e) {
            throw new RouteException(e);
        }
    }
    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);

            //如果這個連接是新建立的,那肯定是健康的,直接返回
            synchronized (connectionPool) {
                if (candidate.successCount == 0) {
                    return candidate;
                }
            }

            //如果不是新創建的,需要檢查是否健康
            if (!candidate.isHealthy(doExtensiveHealthChecks)) {
                // 不健康 關閉連接,釋放Socket,從連接池移除
                // 繼續下次尋找連接操作
                noNewStreams();
                continue;
            }

            return candidate;
        }
    }

這裏是真正的去找一個連接的過程,分爲四步,第一,先判斷上一次的connection是否仍然存在,存在直接返回了;第二,如果不在了,那麼從連接池找,如果找到了,就返回這個連接;第三,還是沒找到,就遍歷所有的路由,再從連接池找一次;第四,最終還沒找到,就創建一個新的連接,並建立socket連接

    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
        boolean foundPooledConnection = false;
        RealConnection result = null;
        Route selectedRoute = null;
        Connection releasedConnection;
        Socket toClose;
        synchronized (connectionPool) {
            .....
            releasedConnection = this.connection;
            toClose = releaseIfNoNewStreams();
            //this.connection就是上一次使用連接時保存的那個connection,在acquire(新建連接)
            //方法中會被賦值,在deallocate方法中會被置空,如果他不爲null,就賦值給result
            if (this.connection != null) {
                // We had an already-allocated connection and it's good.
                result = this.connection;
                releasedConnection = null;
            }
            if (!reportedAcquired) {
                // If the connection was never reported acquired, don't report it as released!
                releasedConnection = null;
            }
            //如果上一次的連接爲空了,嘗試去連接池獲取
            if (result == null) {
                //嘗試從連接池獲取連接,如果有可複用的連接,會給第三個參數 this的connection賦值
                Internal.instance.get(connectionPool, address, this, null);
                //connection != null 說明從連接池找到了連接,否則沒找到
                if (connection != null) {
                    foundPooledConnection = true;
                    result = connection;
                } else {
                    selectedRoute = route;
                }
            }
        }
        closeQuietly(toClose);

        if (releasedConnection != null) {
            eventListener.connectionReleased(call, releasedConnection);
        }
        if (foundPooledConnection) {
            eventListener.connectionAcquired(call, result);
        }

        //經過上邊的兩步,有連接了就直接返回
        if (result != null) {
            // If we found an already-allocated or pooled connection, we're done.
            return result;
        }

        // If we need a route selection, make one. This is a blocking operation.
        //創建一個路由 (dns解析的所有ip與代理的組合)
        boolean newRouteSelection = false;
        if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
            newRouteSelection = true;
            routeSelection = routeSelector.next();
        }
        //走到這裏說明還是沒有找到連接,這個時候遍歷所有路由,再去連接池找一次
        synchronized (connectionPool) {
            if (canceled) throw new IOException("Canceled");

            if (newRouteSelection) {
                // Now that we have a set of IP addresses, make another attempt at getting a
                // connection from
                // the pool. This could match due to connection coalescing.
                //根據代理和不同的ip從連接池中找可複用的連接
                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;
                //new出來一個連接
                result = new RealConnection(connectionPool, selectedRoute);
                acquire(result, false);
            }
        }

        // If we found a pooled connection on the 2nd time around, we're done.
        if (foundPooledConnection) {
            eventListener.connectionAcquired(call, result);
            return result;
        }


        // Do TCP + TLS handshakes. This is a blocking operation.
        // 實際上就是創建socket連接,但是要注意的是如果存在http代理的情況
        result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
                connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());

        Socket socket = null;
        synchronized (connectionPool) {
            reportedAcquired = true;

            //將新創建的連接放到連接池中
            Internal.instance.put(connectionPool, result);

            if (result.isMultiplexed()) {
                socket = Internal.instance.deduplicate(connectionPool, address, this);
                result = connection;
            }
        }
        closeQuietly(socket);

        eventListener.connectionAcquired(call, result);
        return result;
    }

獲取到連接之後就根據相應的協議,創建一個Http1Codec或者Http2Codec,然後執行下一個攔截器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章