OkHttp解析一(分发器)

官网介绍

https://square.github.io/okhttp/
HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.

  • OkHttp is an HTTP client that’s efficient by default:
  • HTTP/2 support allows all requests to the same host to share a
    socket.
  • Connection pooling reduces request latency (if HTTP/2 isn’t
    available).
  • Transparent GZIP shrinks download sizes.
  • Response caching avoids the network completely for repeat requests.

Get a URL
This program downloads a URL and prints its contents as a string.

	String run(String url) throws IOException {
	  Request request = new Request.Builder()
	      .url(url)
	      .build();
	
	  try (Response response = client.newCall(request).execute()) {
	    return response.body().string();
	  }
	}

Post to a Server
This program posts data to a service.

	public static final MediaType JSON
	    = MediaType.get("application/json; charset=utf-8");
	
	OkHttpClient client = new OkHttpClient();
	
	String post(String url, String json) throws IOException {
	  RequestBody body = RequestBody.create(json, JSON);
	  Request request = new Request.Builder()
	      .url(url)
	      .post(body)
	      .build();
	  try (Response response = client.newCall(request).execute()) {
	    return response.body().string();
	  }
	}

调用流程

在这里插入图片描述
OkHttp请求过程中OkHttpClient、Request与Call三个角色是我们经常接触到的。但是所有的逻辑大部分集中在拦截器中,在进入拦截器之前还需要依靠分发器来调配请求任务。
OkHttpClient和Request的创建可以使用它为我们提供的Builder (建造者模式)
Call则是把Request交给OkHttpClient之后返回的一个已准备好执行的请求。
OkHttp在设计时采用的 门面模式,将整个系统的复杂性给隐藏起来,将子系统接口通过一个客户端OkHttpClient统一暴露出来。
OkHttpClient中全是一些配置,比如代理的配置、SSL证书的配置等。
Call本身是一个接口,我们获得的实现为:RealCall。

 static RealCall newRealCall(OkHttpClient client, Request originalRequest,
                                boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.eventListener = client.eventListenerFactory().create(call);
        return call;
    }

Call的execute代表了同步请求,而enqueue则代表异步请求。两者唯一区别在于一个会直接发起网络请求,而另一个使用OkHttp内置的线程池来进行(分发器)。
分发器:Dispatcher,分发器就是来调配请求任务的,内部会包含一个线程池。可以在创建OkHttpClient时,传递我们自己定义的线程池来创建分发器。
拦截器:五大默认拦截器完成整个请求过程。

分发器

成员有

 	private int maxRequests = 64;
    private int maxRequestsPerHost = 5;
    private @Nullable
    Runnable idleCallback;
    /**
     * Executes calls. Created lazily.
     */
    private @Nullable
    ExecutorService executorService;
    /**
     * Ready async calls in the order they'll be run.
     */
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    /**
     * Running asynchronous calls. Includes canceled calls that haven't finished yet.
     */
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    /**
     * Running synchronous calls. Includes canceled calls that haven't finished yet.
     */
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

同步请求
因为同步请求不需要线程池,也不存在任何限制。所以分发器仅做一下记录。

 /**
     * Used by {@code Call#execute} to signal it is in-flight.
     */
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }

异步请求

synchronized void enqueue(AsyncCall call) {
		//正在请求的数量是有限的,默认64
		//用一个域名正在请求的数量也是有限的,默认5
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }

当正在执行的任务未超过最大限制64,同时同一Host的请求不超过5个,则会添加到正在执行队列,同时提交给线程池。否则先加入等待队列。加入线程池直接执行,但是如果加入等待队列后,就需要等待有空闲名额才开始执行。

每次执行完一个请求后,都会调用分发器的finished方法

/**
     * Used by {@code AsyncCall#run} to signal completion.
     */
    void finished(AsyncCall call) {
        finished(runningAsyncCalls, call, true);
    }

    /**
     * Used by {@code Call#execute} to signal completion.
     */
    void finished(RealCall call) {
        finished(runningSyncCalls, call, false);
    }

    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();
        }
    }
    public synchronized int runningCallsCount() {
        return runningAsyncCalls.size() + runningSyncCalls.size();
    }

只有异步任务才会存在限制与等待,所以在执行完了移除后,异步任务结束会执行promoteCalls()。

private void promoteCalls() {
		//如果任务满了直接返回
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        //没有等待执行的任务,返回
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
		//遍历等待执行队列
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall call = i.next();
			//等待任务想要执行,还需要满足:这个等待任务请求的Host不能已经存在5个了
            if (runningCallsForHost(call) < maxRequestsPerHost) {
                i.remove();
                runningAsyncCalls.add(call);
                executorService().execute(call);
            }

            if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
    }

在满足条件下,会把等待队列中的任务移动到runningAsyncCalls并交给线程池执行。分发器到这里就结束了。

请求流程

用户是不需要直接操作任何分发器的,获得的RealCall中就分别提供了execute与enqueue来开始同步请求或异步请求。
同步请求

	@Override
    public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
        	//调用分发器
            client.dispatcher().executed(this);
            //执行请求
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
        	//请求完成
            client.dispatcher().finished(this);
        }
    }

异步请求

 @Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        //调用分发器
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

AsyncCall实际上是一个Runnable的子类,使用线程启动一个Runnable时会执行run方法,在AsyncCall中被重定向到execute方法。
同时AsyncCall也是RealCall的普通内部类,这意味着它是持有外部类RealCall的引用,可以获得直接调用外部类的方法。

final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

        AsyncCall(Callback responseCallback) {
            super("OkHttp %s", redactedUrl());
            this.responseCallback = responseCallback;
        }

        String host() {
            return originalRequest.url().host();
        }

        Request request() {
            return originalRequest;
        }

        RealCall get() {
            return RealCall.this;
        }

        @Override
        protected void execute() {
            boolean signalledCallback = false;
            try {
                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) {
                if (signalledCallback) {
                    // Do not signal the callback twice!
                    Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
                } else {
                    eventListener.callFailed(RealCall.this, e);
                    responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                client.dispatcher().finished(this);
            }
        }
    }
/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
    protected final String name;

    public NamedRunnable(String format, Object... args) {
        this.name = Util.format(format, args);
    }

    @Override
    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
            execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }
    }

    protected abstract void execute();
}

可以看到无论是同步还是异步请求实际上真正执行请求的工作都在getResponseWithInterceptorChain()中。这个方法就是整个OkHttp的核心:拦截器 (责任链模式)。

Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        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, this, eventListener, client.connectTimeoutMillis(),
                client.readTimeoutMillis(), client.writeTimeoutMillis());

        return chain.proceed(originalRequest);
    }

分发器线程池

分发器中默认的线程池

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;
}

和Executors.newCachedThreadPool()创建的线程一样。

当一个任务通过execute(Runnable)方法添加到线程池时:

  • 线程数量小于corePoolSize,新建线程(核心)来处理被添加的任务;
  • 线程数量大于等于 corePoolSize,存在空闲线程,使用空闲线程执行新任务;
  • 线程数量大于等于 corePoolSize,不存在空闲线程,新任务被添加到等待队列,添加成功则等待空闲线程,添加失败:
    • 线程数量小于maximumPoolSize,新建线程执行新任务;
    • 线程数量等于maximumPoolSize,拒绝此任务。

核心线程为0,表示线程池不会一直为我们缓存线程,线程池中所有线程都是在60s内没有工作就会被回收。而最大线程Integer.MAX_VALUE与等待队列SynchronousQueue的组合能够得到最大的吞吐量。即当需要线程池执行任务时,如果不存在空闲线程不需要等待,马上新建线程执行任务!等待队列的不同指定了线程池的不同排队机制。一般来说,等待队列BlockingQueue有:ArrayBlockingQueue、LinkedBlockingQueue与SynchronousQueue。

分发器常问问题

如何决定将请求放入ready还是running?
如果当前正在请求数不小于64放入ready;如果小于64,但是已经存在同一域名主机的请求5个放入ready。
从running移动ready的条件是什么?
每个请求执行完成就会从running移除,同时进行第一步相同逻辑的判断,决定是否移动。
分发器线程池的工作行为?
无等待,最大并发。SynchronousQueue : 无容量的队列。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值