美文网首页Android
网络访问_OKHttp

网络访问_OKHttp

作者: gczxbb | 来源:发表于2018-03-05 17:34 被阅读13次

    OKHttp技术结构

    OKHttp技术点.jpg

    建造者设计模式创建OKHttpClient客户端,当与Retrofit一起使用时,Retrofit建造者Build#client方法将OkHttpClient传给Retrofit。


    1. Okhttp线程池与任务分发

    一个完整的Http请求从App客户端发送到服务器,不占用主线程资源,在多个请求并发下条件,线程池负责分配线程,执行请求(任务)。客户端将请求封装成Request
    Okhttp任务分发示意图如下所示:

    Okhttp任务分发与线程池.jpg

    1.1 请求者RealCall

    OkHttp为每一个请求创建一个RealCall

    OkHttpClient#newCall方法。

    @Override
    public Call newCall(Request request) {
        return new RealCall(this, request, false /* for web socket */);
    }
    

    Call是接口类型,内部工厂接口Factory,Factory#newCall方法负责创建Call,OkHttpClient实现Call.Factory工厂接口。
    RealCall负责一个具体请求的Http事务,同步或异步,交给OkHttpClient的Dispatcher分发处理
    RealCall#execute方法。同步请求。

    @Override
    public Response execute() throws IOException {
        synchronized (this) {
        ...
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();//同步,耗时处
          return result;
        } finally {
          client.dispatcher().finished(this);
        }
    }
    

    RealCall#enqueue方法。异步请求。

    @Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
        ...
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    

    1.2 分发者Dispatcher

    Dispatcher控制多个Http请求的节奏,负责RealCall的并发,等待与运行队列管理,线程池调度。

    • 异步请求分发
      Dispatcher#enqueue方法。
    synchronized void enqueue(AsyncCall call) {
        //maxRequests=64,maxRequestsPerHost=5
        if (runningAsyncCalls.size() < maxRequests && 
                runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);//加入运行队列
          executorService().execute(call);//线程池执行任务
        } else {
          readyAsyncCalls.add(call);
        }
    }
    

    maxRequestsmaxRequestsPerHost决定等待还是处理。
    maxRequests:最大请求数量。
    maxRequestsPerHost:每个主机最大并发请求数量。
    若满足并发数量,将AsyncCall任务加入运行队列,交给线程池处理。
    若不满足并发数量,将AsyncCall任务加入等待队列。
    Dispatcher内部线程池调度任务,AsyncCall是RealCall的内部类,Runnable类型。

    • 同步请求分发

    Dispatcher#executed方法。

    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }
    

    当前线程执行同步请求,同步队列保存RealCall,而不是Runnable任务
    将RealCall加入同步队列,代表这个RealCall正在执行,耗时点在RealCall#getResponseWithInterceptorChain方法

    • 请求结束

    Dispatcher#finish方法。

    //异步
    void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
    }
    
    //同步
    void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }
    

    同步请求结束时,从同步队列中删除RealCall。

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) {
          if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
          if (promoteCalls) promoteCalls();//异步
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
    }
    

    异步请求结束时,从运行队列中删除AsyncCall任务,然后触发Dispatcher#promoteCalls方法。
    Dispatcher#promoteCalls方法。

    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return;
        if (readyAsyncCalls.isEmpty()) return; 
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall call = i.next();
            if (runningCallsForHost(call) < maxRequestsPerHost) {
                i.remove();//满足条件,等待队列删除
                runningAsyncCalls.add(call);//加入运行中队列
                executorService().execute(call);//开始执行
            }
            if (runningAsyncCalls.size() >= maxRequests) return; 
        }
    }
    

    查找等待队列的任务,遍历,满足并发数量条件时,每个等待队列的任务都会处理,从等待队列删除,加入运行队列,交给线程池。不满足并发条件时,不处理。

    每次异步/同步请求结束后,均调用Dispatcher#finish方法,异步请求在AsyncCall#execute最后调用,异步让等待中的任务得到机会

    RealCall请求执行流程图如下所示。 RealCall请求执行流程示意图.jpg

    1.3 Okhttp线程池(缓存线程池)

    Dispatcher#executorService方法。

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, 
                    TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), 
                    Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
    }
    

    ThreadPoolExecutor是一个阀值[0, Integer.MAX_VALUE]的线程池。

    • 不保留corePoolSize核心线程,最大可创建线程MAX_VALUE。
    • 每个工作线程keepAliveTime设置60s,保持60s存活期,线程复用
    • SynchronousQueue阻塞队列不存储元素,是一个管道。

    大量并发请求时,线程池启动多个工作线程,确保每个任务都有线程及时处理,工作线程都是临时线程。每个线程执行NamedRunnable#run方法,AsyncCall继承NamedRunnable,实现execute抽象方法。
    AsyncCall#execute方法。

    @Override
    protected void execute() {
        boolean signalledCallback = false;
        try {
            //触发RealCall的方法,AsyncCall定义在RealCall类内部。
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
                signalledCallback = true;
                responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
                signalledCallback = true;
                responseCallback.onResponse(RealCall.this, response);
            }
        } catch (IOException e) {
            ....
        } finally {
            //异步请求结束后finish。
            client.dispatcher().finished(this);
        }
    }
    

    RealCall#getResponseWithInterceptorChain方法,同步和异步都会触发,耗时操作,获取Response。
    AsyncCall构造方法创建时,将外部Callback传入,获取Response成功或失败均回调Callback。


    2. OKHttp拦截器设计

    拦截器列表如下图所示 Interceptor列表.jpg

    通过创建一系列拦截器链式处理获取Response。缓存,链路连接都通过责任链设计实现。

    RealCall#getResponseWithInterceptorChain方法。

    Response getResponseWithInterceptorChain() throws IOException {
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        interceptors.add(new CacheInterceptor(client.internalCache()));
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
          interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));
    
        Interceptor.Chain chain = new RealInterceptorChain(
            interceptors, null, null, null, 0, originalRequest);
        return chain.proceed(originalRequest);
    }
    

    在拦截器之间传递的链对象RealInterceptorChain,index初始是0,传入原始请求originalRequest与拦截器列表interceptors。
    触发RealInterceptorChain#proceed方法,在proceed方法中找到interceptors链表index索引处的拦截器,第index个拦截器Interceptor,新建一个index++的RealInterceptorChain对象,触发Interceptor#intercept方法,传递Chain。

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, 
                        RealConnection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
        calls++;
        ..
        RealInterceptorChain next = new RealInterceptorChain(
            interceptors, streamAllocation, httpCodec, connection, index + 1, request);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
    
        if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
            //抛出异常
        }
        if (response == null) {
            //抛出异常
        }
        return response;
    }
    

    每一个拦截器Interceptor#intercept方法。

    • 处理Request。
    • 触发Chain#proceed方法。
    • 处理Response。

    每一个Chain#proceed方法。

    • 找到下一个拦截器。
    • 创建一个新RealInterceptorChain。
    • 触发Interceptor#intercept传递给Interceptor。

    一直递归调用,当index++到最后一个CallServerInterceptor拦截器,真正向服务器发送数据,获取Response,然后递归一层层按原路返回Response。

    拦截器责任链设计是一个递归调用的过程。

    Okhttp拦截器执行流程.png

    Chain是链传递的对象,每个Chain在proceed处理时,创建另一个index增1的Chain链对象,传递给下一项Interceptor
    每一项Interceptor在intercept时,先拦截处理Request,再调用Chain的proceed方法,最后处理Response
    责任链设计类。

    public interface Interceptor {
        Response intercept(Chain chain) throws IOException;
    
        interface Chain {
            Request request();
    
            Response proceed(Request request) throws IOException;
    
            Connection connection();
        }
    }
    

    proceed方法流入Request,流出Response。每一个拦截器的目的是层层截断流入与流出,先截流,加工处理,再放流。


    3. OKHttp缓存设计

    拦截器CacheInterceptor处理缓存。

    3.1 缓存接口InternalCache

    Cache是Okhttp设计的缓存类,内部定义了缓存接口InternalCache的实现,在Cache中包含一些与InternalCache相同的方法。
    Cache内部实现的InternalCache接口。

    final InternalCache internalCache = new InternalCache() {
        @Override
        public Response get(Request request) throws IOException {
            return Cache.this.get(request);
        }
        @Override 
        public CacheRequest put(Response response) throws IOException {
            return Cache.this.put(response);
        }
        @Override 
        public void remove(Request request) throws IOException {
            Cache.this.remove(request);
        }
        ...
      };
    

    InternalCache接口设计的方法用于对外暴漏Cache缓存方法的实现。

    在拦截器CacheInterceptor创建时,使用OkHttpClient中的缓存。OkHttpClient中包含Cache与InternalCache。OkHttpClient#internalCache方法,优先使用OkHttpClient中Cache的internalCache,其次是internalCache。
    OkHttpClient#internalCache方法。

    final Cache cache;
    final InternalCache internalCache;
    
    InternalCache internalCache() {
        return cache != null ? cache.internalCache : internalCache;
    }
    

    DiskLruCache存储缓存。

    3.2 缓存策略

    CacheInterceptor#intercept方法。

    @Override 
    public Response intercept(Chain chain) throws IOException{
        Response cacheCandidate = cache != null
            ? cache.get(chain.request())
            : null;
        long now = System.currentTimeMillis();
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;
        ....
    }
    

    首先根据InternalCache查找缓存Response。
    创建CacheStrategy#Factory工厂,负责创建缓存策略CacheStrategy。Factory是CacheStrategy内部类,构造方法传入now(当前时间),request(发送请求),cacheCandidate(缓存Response)。
    CacheStrategy#Factory#get方法。

    public CacheStrategy get() {
        CacheStrategy candidate = getCandidate();
        if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
            return new CacheStrategy(null, null);
        }
        return candidate;
    }
    

    Factory中存储Request、cacheResponse以及从Response的Header解析出来的servedDate、expires。Factory#getCandidate方法根据他们的值创建缓存策略。

    CacheStrategy#Factory#getCandidate方法。

    private CacheStrategy getCandidate() {
        if (cacheResponse == null) {//1
            return new CacheStrategy(request, null);
        }
    
        if (request.isHttps() && cacheResponse.handshake() == null) {//2
            return new CacheStrategy(request, null);
        }
    
        if (!isCacheable(cacheResponse, request)) {//3
            return new CacheStrategy(request, null);
        }
    
        CacheControl requestCaching = request.cacheControl();
        if (requestCaching.noCache() || hasConditions(request)) {//4
            return new CacheStrategy(request, null);
        }
        ...
    }
    

    几类简单的策略

    • Response缓存是空,说明未存储缓存,策略中仅包含Request,不包含缓存Response。
    • Request是https协议并且Response的handshake是空,策略中仅包含Request。
    • CacheControl设置为noStore状态,说明不存储,策略中仅包含Request。
    • 请求中包含noCache标志,或者请求中Header的If-Modified-Since或If-None-Match非空,策略中仅包含Request。
    • 当小于最大期限时,使用缓存,策略中仅包含Response。

    根据CacheStrategy策略内部networkRequest与cacheResponse,若无需访问网络,则构建Response返回,若需要访问网络,继续责任链调用,下一个拦截器是ConnectInterceptor,建立访问链路,最后获取的Response会视情况存入Cache。


    4. OKHttp连接链路

    拦截器ConnectInterceptor连接链路。

    4.1 StreamAllocation

    ConnectInterceptor#intercept方法。

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();//从Chain中获取Request
        StreamAllocation streamAllocation = realChain.streamAllocation();
    
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
        //处理返回Response
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    
    • StreamAllocation在RetryAndFollowUpInterceptor#intercept方法中创建,在Chain#proceed中传递给下一个Chain对象,当Chain到达ConnectInterceptor时,从Chain中获取。

    RetryAndFollowUpInterceptor#intercept方法代码段。

    streamAllocation = new StreamAllocation(
            client.connectionPool(), createAddress(request.url()), callStackTrace);
    

    每个RealCall对应一个拦截器列表(多个拦截器对象),因此,每个RealCall分配一个StreamAllocation。负责具体网络链路分配、建立和访问

    StreamAllocation#newStream方法。

    public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
        ...
        try {
            RealConnection resultConnection = findHealthyConnection(connectTimeout, 
                    readTimeout,writeTimeout, connectionRetryEnabled, 
                    doExtensiveHealthChecks);
            HttpCodec resultCodec = resultConnection.newCodec(client, this);
    
            synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
          }
        } catch (IOException e) {
        }
    }
    
    • newStream方法通过分配策略寻找一个真正的链接RealConnection,初始化HttpCodec对象。

    • 最后Chain#proceed方法将RealConnection,HttpCodec和StreamAllocation传递给下一个拦截器CallServerInterceptor。

    StreamAllocation#findConnection方法。

    private RealConnection findConnection(int connectTimeout, int readTimeout, 
                int writeTimeout,boolean connectionRetryEnabled) throws IOException {
        Route selectedRoute;
        synchronized (connectionPool) {
            //先使用StreamAllocation内部保存的RealConnection
            RealConnection allocatedConnection = this.connection;
            if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
                return allocatedConnection;
            }
    
            //连接池中寻找
            Internal.instance.get(connectionPool, address, this, null);
            if (connection != null) {
                return connection;
            }
            selectedRoute = route;
        }
        ...
        RealConnection result;
        synchronized (connectionPool) {
            Internal.instance.get(connectionPool, address, this, selectedRoute);
            if (connection != null) return connection;
            //创建RealConnection
            route = selectedRoute;
            refusedStreamCount = 0;
            result = new RealConnection(connectionPool, selectedRoute);
            acquire(result);
        }
    
        result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
        routeDatabase().connected(result.route());
    
        Socket socket = null;
        synchronized (connectionPool) {
          //入池
          Internal.instance.put(connectionPool, result);
          ...
        }
        closeQuietly(socket);
        return result;
      }
    

    分配获取策略

    • 优先考虑StreamAllocation内部保存的RealConnection连接。
    • 其次从连接池ConnectionPool中获取。
    • 创建RealConnection,保存在StreamAllocation内部,入池。

    OkHttpClient#Internal对象,(静态代码段)。

    static {
        Internal.instance = new Internal() {
            @Override public 
            RealConnection get(ConnectionPool pool, Address address,StreamAllocation streamAllocation, Route route) {
                return pool.get(address, streamAllocation, route);
            }
            @Override 
            public void put(ConnectionPool pool, RealConnection connection) {
                pool.put(connection);
            }
            ....//其他方法.
        };
    }
    

    从ConnectionPool连接池中获取RealConnection。
    StreamAllocation#acquire方法。

    public void acquire(RealConnection connection) {
        ..
        this.connection = connection;
        connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
    }
    

    RealConnection内部存储一个引用它的StreamAllocation的弱引用列表,多个StreamAllocation公用RealConnection连接链路。

    从连接池获取时,RealConnection的allocationLimit限制StreamAllocation弱引用数量,遍历连接池中每个RealConnection,若Address相等且StreamAllocationReference数量小于限制,则该连接可用。若RealConnection的限制数量已达到allocationLimit,该链接不能再被StreamAllocation使用。

    将获取的RealConnection返回,设置成StreamAllocation内部connection,然后将引用它的StreamAllocation加入allocations弱引用列表,代表又一个StreamAllocation使用了该链路。
    若不满足条件池中连接不可用,新建链路,新建的RealConnection同样赋值给StreamAllocation内部,并将StreamAllocation加入弱引用列表,最后入连接池。
    OkHttp连接链路意图如下所示。

    Okhttp连接池与链路.jpg

    StreamAllocation负责管理RealConnection、HttpCodec,操作连接池,分配、取消、释放连接。多个请求StreamAllocation可复用RealConnection

    4.2 RealConnection

    RealConnection代表一个真正的链路,功能包括BufferedSource,BufferedSink,路由,socket,协议,Handshake信息。

    建立Socket连接
    新建RealConnection先执行RealConnection#connect方法,触发connectSocket,建立连接。
    RealConnection#connectSocket方法。

    private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
        Proxy proxy = route.proxy();
        Address address = route.address();
        //创建Socket
        rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
            ? address.socketFactory().createSocket()
            : new Socket(proxy);
        rawSocket.setSoTimeout(readTimeout);
        try {
          Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
        } catch (ConnectException e) {
          ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
          ce.initCause(e);
          throw ce;
        }
        source = Okio.buffer(Okio.source(rawSocket));
        sink = Okio.buffer(Okio.sink(rawSocket));
    }
    

    Platform#connectSocket触发Socket#connect方法,连接Socket,若连接成功,与服务器建立一个Socket连接,若连接失败抛出异常。
    Platform#connectSocket方法。

    public void connectSocket(Socket socket, InetSocketAddress address, int connectTimeout) throws IOException {
        socket.connect(address, connectTimeout);
    }
    

    Okio根据Socket获取读写缓冲区,负责具体读写底层Request与Response的Header与Body消息,由Okio框架的BufferedSourceBufferedSink完成。
    创建HttpCodec
    RealConnection#newCodec方法。

    public HttpCodec newCodec(OkHttpClient client, StreamAllocation streamAllocation) 
                throws SocketException {
        if (http2Connection != null) {
            return new Http2Codec(client, streamAllocation, http2Connection);
        } else {
            socket.setSoTimeout(client.readTimeoutMillis());
            source.timeout().timeout(client.readTimeoutMillis(), MILLISECONDS);
            sink.timeout().timeout(client.writeTimeoutMillis(), MILLISECONDS);
            return new Http1Codec(client, streamAllocation, source, sink);
        }
    }
    

    如果协议采用Protocol.HTTP_2,则Http2Connection存在,创建Http2Codec,否则创建Http1Codec,将BufferedSource和BufferedSink封装到Http1Codec。

    4.3 ConnectionPool连接池管理

    ConnectionPool连接池负责链路管理,默认支持5个并发keepalive,默认链路生命为5分钟,即链路数据传输完毕后可保持5分钟空闲的存活时间。

    自动清除线程会查找超过5分钟的链路,关闭链路即关闭socket。

    • ArrayDeque双向队列保存RealConnection连接。线性连续空间,双向开口,可高效在头尾两端插入删除,同时具有队列和栈性质,常用缓存。
    • get/put连接池操作方法,ConnectionPool#put时触发清理任务cleanupRunnable

    ConnectionPool#put方法。

    void put(RealConnection connection) {
        if (!cleanupRunning) {
          cleanupRunning = true;
          executor.execute(cleanupRunnable);//执行清理任务
        }
        connections.add(connection);//加入连接池队列
    }
    
    • 线程池负责执行cleanupRunnable任务,清理连接,put操作时,若cleanupRunning标志位是false,说明任务未被执行,主动调用,进入while死循环(除非主动退出),实质上是一个阻塞的清理任务。若任务循环未主动退出,下次put将不会触发。

    cleanupRunnable任务#run方法。

    while (true) {
        long waitNanos = cleanup(System.nanoTime());
        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) {
                }
            }
        }
    }
    

    首先cleanup清理方法,返回值waitNanos是下次清理的间隔时间,然后调用wait(timeout)等待,释放锁与时间片。等待waitNanos时间后,继续while循环cleanup清理。

    waitNanos代表等待下一次清理的时间间隔,-1表示不需要再次清理,退出循环,0表示立即再次清理。

    ConnectionPool清理任务流程图如下所示。 ConnectionPool清理任务处理流程.jpg

    ConnectionPool#cleanup方法代码段。

    for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
        RealConnection connection = i.next();
        // 是否在使用
        if (pruneAndGetAllocationCount(connection, now) > 0) {
            inUseConnectionCount++;
            continue;
        }
        idleConnectionCount++;
        //找空闲最长的
        long idleDurationNs = now - connection.idleAtNanos;
        if (idleDurationNs > longestIdleDurationNs) {
            longestIdleDurationNs = idleDurationNs;
            longestIdleConnection = connection;
        }
    }
    

    遍历,pruneAndGetAllocationCount方法查看连接是否在使用。inUseConnectionCount:正在使用数量。若正在使用,自增。idleConnectionCount:空闲的数量。若连接未使用,自增。idleDurationNs:空闲的连接,计算已经空闲时间,找到空闲时间最长的链接
    ConnectionPool#cleanup方法代码段。

    if (longestIdleDurationNs >= this.keepAliveDurationNs
              || idleConnectionCount > this.maxIdleConnections) {
        connections.remove(longestIdleConnection);
    } else if (idleConnectionCount > 0) {
        return keepAliveDurationNs - longestIdleDurationNs;
    } else if (inUseConnectionCount > 0) {
        return keepAliveDurationNs;
    } else {
        cleanupRunning = false;
        return -1;
    }
    closeQuietly(longestIdleConnection.socket());
    return 0;
    
    • 若longestIdleDurationNs(最长空闲时间)大于5分钟或空闲连接数量超过5(默认),则将该链接删除,返回0,下次立即清理
      -若最长空闲时间不足5分钟,但有空闲连接(未大于5个),返回剩余时间timeout(距离5分钟还有多久),清理线程阻塞timeout再次清理。
    • 若不存在空闲链接,且存在使用链接,返回最大等待时间5分钟
    • 空闲和正在使用的连接均不存在,无需清理,返回-1,线程退出。设置任务cleanupRunning标志位false,下次put会重新唤起该任务

    清理过程

    • 从连接池队列ArrayDeque中删除该项连接。
    • closeQuietly关闭socket。

    判断连接正在使用
    找到RealConnection内部Reference<StreamAllocation>列表,遍历,若reference.get()存在,说明有StreamAllocation正引用RealConnection,正在使用,若reference.get()不存在,说明StreamAllocation已经被Jvm清理,同时从references列表中删除该项reference,遍历完毕,若references列表为空,说明references的弱引用都不存在,没有StreamAllocation使用该连接。

    5. OkHttp数据流

    拦截器CallServerInterceptor负责网络数据流读写。

    ConnectInterceptor拦截器利用StreamAllocation创建RealConnection(或复用)与HttpCodec,通过Chain#proceed方法封装到一个新Chain对象,传递给下一个拦截器CallServerInterceptor。
    CallServerInterceptor#intercept方法。

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        //获取前一个拦截器ConnectInterceptor创建的HttpCpdec
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();
    
        long sentRequestMillis = System.currentTimeMillis();
        //利用BufferSink写入
        httpCodec.writeRequestHeaders(request);
    
        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            ...
        }
        //Sink的flush
        httpCodec.finishRequest();
        //利用Source读取Response的Header
        if (responseBuilder == null) {
          responseBuilder = httpCodec.readResponseHeaders(false);
        }
    
        Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
    
        int code = response.code();
        ...
        ...
        return response;
    }
    

    通过底层IO操作库Okio,HttpCodec封装了BufferedSource和BufferedSink,负责数据流缓冲区的读写操作,数据源是RealConnection的Socket。BufferedSink负责写入,BufferedSource负责读取。


    任重而道远

    相关文章

      网友评论

        本文标题:网络访问_OKHttp

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