39. OkHttp之-拦截器-ConnectIntercept

2021-03-19  本文已影响0人  任振铭

这个拦截器的职责很简单,就是获取一份和服务器的连接,具体来说就是一个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,然后执行下一个拦截器。

上一篇下一篇

猜你喜欢

热点阅读