OkHttp源码流程简单分析

OkHttp用法

// 接入依赖
	implementation 'com.squareup.okhttp3:okhttp:3.8.0'
    implementation 'com.squareup.okio:okio:1.12.0'
	
// 基本用法
	
	// 创建OkHttpClient并设置拦截器、超时,是建造者模式
	val client = OkHttpClient.Builder()
            .addInterceptor {
                return@addInterceptor it.proceed(it.request())
            }
            .connectTimeout(60, TimeUnit.SECONDS)
            .build()
    
    // 创建请求对象,并设置请求头、方法、地址、body,是建造者模式        
    val request = Request.Builder()
            .addHeader("","")
            .post(RequestBody)
            .url("")
            .build()
    
    // 执行异步请求
    client.newCall(request).enqueue(object:Callback{
            override fun onFailure(call: Call, e: IOException) {
            }

            override fun onResponse(call: Call, response: Response) {
            }
    })

流程分析(以异步请求为例)

  • client.newCall(request).enqueue(Callback),开启一个异步网络请求,内部是执行一个client.dispatcher.enqueue()方法
    @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    
  • client.dispatcher,我们可以暂且理解为线程池的工具类;内部维护了一个无核心线程且存活时长只有60s的线程;并且限定了默认最大的请求数是64,相同域名的最大请求数是5
    public final class Dispatcher {
      // 最大请求并发数
      private int maxRequests = 64;
      // 相同域名的最大并发数
      private int maxRequestsPerHost = 5;
      private @Nullable ExecutorService executorService;
    
      public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
      }
    
      public Dispatcher() {
      }
    
      // 创建线程池
      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;
      }
    }
    
  • client.dispatcher.enqueue(AsyncCall),在其内部主要是对请求任务的分派到线程池中执行/等待执行
    synchronized void enqueue(AsyncCall call) {
    	// 注释1:校验并发请求数量
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          // 注释2:将请求任务添加到线程池中
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
    }
    
  • AsyncCall.execute(),AsyncCall是Runnable子类,当线程池运行后,会执行其exectue(),在其内部主要是干了一件大事,就是按顺序执行了拦截器,执行拦截器是通过责任链式的设计模式,等待所有拦截器执行完毕后,会通过Callback将执行的结果回调给用户
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
     	// 注释1:执行拦截器,并接收拦截器对下发响应的处理
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          // 注释2:请求失败
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          // 注释3:请求成功
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
    
  • getResponseWithInterceptorChain(),内部就是先整理我们自定义的拦截器,然后是RetryAndFollowUpInterceptor重试/重定向拦截器、BirdgeInterceptor桥接拦截器、CacheInterceptor缓存拦截器、ConnectInterceptor连接拦截器、CallServerInterceptor服务回调拦截器;其内部的玩法主要是通过创建RealInterceptorChain对象,并且持有待执行的拦截器所在的索引,然后通过chain.proceed(request),在其内部会再次创建一个RealInterceptorChain对象,并持有下一个待执行的拦截器索引,然后执行当前拦截器的intercept(chain)并返回chain.proceed(request)即类似递归效应,直至所有拦截器全部执行完毕
    Response getResponseWithInterceptorChain() throws IOException {
    	// 注释1:整理拦截器,并按顺序执行
        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));
    
    	// 注释2:创建RealInterceptorChain,并执行proceed
        Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);
        return chain.proceed(originalRequest);
    }
    
    // 注释3:内部创建RealInterceptorChain并持有下一个拦截器的引用
    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
        ...
        // 注释4:创建RealInterceptorChain,并获取到下一个拦截器所在的索引,待取出下一个拦截器进行执行
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
    	...
        return response;
    }
    
    考点:责任链式模式的优点?更大程度上实现功能性的解藕,当删除链上的任意节点,对整个链的运行不受影响
  • RetryAndFollowUpInterceptor:重试/重定向拦截器;作用是对于后面拦截器执行出现的异常如:路由异常、服务连接异常等会进行重新尝试运行后面拦截器;如果是返回了需要重定向的code,则会进行url的重新拼装,重新尝试执行后面拦截器,但是重定向是有次数限制的,不超过20次
  • BirdgeInterceptor:桥接拦截器;作用是对报文数据的处理,如:请求头的拼接、报文的压缩、以及对下发的Response的解压等操作
  • CacheInterceptor:缓存拦截器;作用就是当条件符合的情况下,从缓存中取数据,如:当支持缓存的情况下,无网络,且有缓存,则取缓存中的数据、当下发的code是304,表示未被修改的,则取的是缓存中的,可能会对响应头的数据刷新缓存、还有就是对于,数据的修改得到的响应是不做缓存处理的,如:post、put、delete等请求
  • ConnectInterceptor:连接拦截器;作用就是获取一个可以用于连接的服务器的链接,先是从链接池中寻找一个可用的链接,通过遍历链接池,与在目标请求地址做匹配,当域名和端口一致时,就可以复用该链接;如果没有找到可用的链接时,则创建一个新的链接RealConnect,然后通过socket开启链接主机,并将新的链接保存到链接池中
    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
        Route selectedRoute;
        synchronized (connectionPool) {
        	...
          // 注释1:从链接池中遍历,找到合适的链接
          Internal.instance.get(connectionPool, address, this, null);
          if (connection != null) {
            return connection;
          }
        }
    	...
    	// 注释2:链接池中没有可以用的链接,则创建一个链接
    	result = new RealConnection(connectionPool, selectedRoute);
        ...
        // 注释3:建立链接,通过socket
        result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
        routeDatabase().connected(result.route());
    
        Socket socket = null;
        synchronized (connectionPool) {
          // 注释4:添加到链接池中,并且会开启一个线程池任务监听每个链接的空闲状态,是否需要被清理
          Internal.instance.put(connectionPool, result);
          if (result.isMultiplexed()) {
            socket = Internal.instance.deduplicate(connectionPool, address, this);
            result = connection;
          }
        }
        closeQuietly(socket);
        return result;
    }
    
    // 从链接池中找一个可用的链接
    @Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
        assert (Thread.holdsLock(this));
        for (RealConnection connection : connections) {
    	  // 匹配域名和端口一致的链接
          if (connection.isEligible(address, route)) {
            streamAllocation.acquire(connection);
            return connection;
          }
        }
        return null;
    }
    
    public boolean isEligible(Address address, @Nullable Route route) {
        // 校验当前是否允许再创建链接流
        if (allocations.size() >= allocationLimit || noNewStreams) return false;
    
        // 域名跟端口一致的链接,即为可用
        if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;
        if (address.url().host().equals(this.route().address().url().host())) {
          return true; // This connection is a perfect match.
        }
    
    	// 下面的匹配不太清楚
        // At this point we don't have a hostname match. But we still be able to carry the request if
        // our connection coalescing requirements are met. See also:
        // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
        // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/
    
        // 1. This connection must be HTTP/2.
        if (http2Connection == null) return false;
    
        // 2. The routes must share an IP address. This requires us to have a DNS address for both
        // hosts, which only happens after route planning. We can't coalesce connections that use a
        // proxy, since proxies don't tell us the origin server's IP address.
        if (route == null) return false;
        if (route.proxy().type() != Proxy.Type.DIRECT) return false;
        if (this.route.proxy().type() != Proxy.Type.DIRECT) return false;
        if (!this.route.socketAddress().equals(route.socketAddress())) return false;
    
        // 3. This connection's server certificate's must cover the new host.
        if (route.address().hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
        if (!supportsUrl(address.url())) return false;
    
        // 4. Certificate pinning must match the host.
        try {
          address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
        } catch (SSLPeerUnverifiedException e) {
          return false;
        }
    
        return true; // The caller's address can be carried by this connection.
    }
    
  • Internal.instance.put(connectionPool, result);:将新建的链接添加到链接池中,并且会在需要的时候开启任务去监听链接池中链接的空闲状态是否需要被清理,具体如何清理超时的空闲流程,代码注释中已表明清楚
    void put(RealConnection connection) {
        assert (Thread.holdsLock(this));
        if (!cleanupRunning) {
          cleanupRunning = true;
          // 注释1:将清理任务添加到线程池中
          executor.execute(cleanupRunnable);
        }
        // 注释2:将链接添加到链接池中
        connections.add(connection);
    }
    
    private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
      	// 注释3:检查每个链接是否超时,并从链接池中清理,以及关闭链接socket
        long waitNanos = cleanup(System.nanoTime());
        // 注释4:当链接池中无链接时,退出监听任务
        if (waitNanos == -1) return;
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
              // 注释4:等待即将要超时的链接,然后重新去检查,并处理超时的链接
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
      }
    }
    };
    
    long cleanup(long now) {
        int inUseConnectionCount = 0;
        int idleConnectionCount = 0;
        RealConnection longestIdleConnection = null;
        long longestIdleDurationNs = Long.MIN_VALUE;
        synchronized (this) {
          for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
            RealConnection connection = i.next();
            // 注释5:判断当前链接是否处于空闲,并计算当前的的空闲时长
            if (pruneAndGetAllocationCount(connection, now) > 0) {
              inUseConnectionCount++;
              continue;
            }
            // 注释6:空闲链接计数器
            idleConnectionCount++;
            
            long idleDurationNs = now - connection.idleAtNanos;
            if (idleDurationNs > longestIdleDurationNs) {
              longestIdleDurationNs = idleDurationNs;
              longestIdleConnection = connection;
            }
          }
    
    	  // 注释7:判断当前链接已经超时,直接移除链接池
          if (longestIdleDurationNs >= this.keepAliveDurationNs
              || idleConnectionCount > this.maxIdleConnections) {
            connections.remove(longestIdleConnection);
            
            // 注释8:存在空闲链接,但是还未超时,则返回一个剩余时长
          } else if (idleConnectionCount > 0) {
            return keepAliveDurationNs - longestIdleDurationNs;
    		
    		// 注释9:正在使用的链接,返回他的时长为5分钟
          } else if (inUseConnectionCount > 0) {
            return keepAliveDurationNs;
    		
    		// 注释10:表示此时链接池已无链接,返回-1,即将关闭监听任务
          } else {
            cleanupRunning = false;
            return -1;
          }
        }
        // 注释11:对已经超时的链接进行关闭
        closeQuietly(longestIdleConnection.socket());
        return 0;
    }
    
  • CallServerInterceptor:回调服务拦截器:作用就是通过ConnectInterceptor建立的链接得到的流,进行与服务器的通信,发送requestBody,以及等待Response的响应,然后将从流中读取到的数据转化,并构建出Response往上面的拦截器发送,交由他们处理,最终回到用户端
    public final class CallServerInterceptor implements Interceptor {
      private final boolean forWebSocket;
    
      @Override public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();
    
        long sentRequestMillis = System.currentTimeMillis();
        httpCodec.writeRequestHeaders(request);
    
        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
          if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
            httpCodec.flushRequest();
            responseBuilder = httpCodec.readResponseHeaders(true);
          }
    
          if (responseBuilder == null) {
            Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
            BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
            // 注释1:往服务器写请求数据
            request.body().writeTo(bufferedRequestBody);
            bufferedRequestBody.close();
          } else if (!connection.isMultiplexed()) {
            streamAllocation.noNewStreams();
          }
        }
    
        httpCodec.finishRequest();
    
        if (responseBuilder == null) {
          responseBuilder = httpCodec.readResponseHeaders(false);
        }
    
        Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
    
    	// 注释2:去读到下发的数据,并构建Response
        int code = response.code();
        if (forWebSocket && code == 101) {
        response = response.newBuilder()
              .body(Util.EMPTY_RESPONSE)
              .build();
        } else {
          response = response.newBuilder()
              .body(httpCodec.openResponseBody(response))
              .build();
        }
        ...
        return response;
      }
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值