美文网首页
39. OkHttp之-拦截器-ConnectIntercept

39. OkHttp之-拦截器-ConnectIntercept

作者: 任振铭 | 来源:发表于2021-03-19 08:32 被阅读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,然后执行下一个拦截器。

    相关文章

      网友评论

          本文标题:39. OkHttp之-拦截器-ConnectIntercept

          本文链接:https://www.haomeiwen.com/subject/fvxucltx.html