OkHttp架构—异步请求enqueue(不完整篇)

简介: 我分为了四个部分,橙色第一部分实例化一个OkHttoClient类对象就可以了。所有的逻辑大部分在拦截器Interceptors中,但进入拦截器之前还要靠分发器来调配请求任务。分发器Dispatcher:内部维护队列和线程池,完成请求调配。拦截器Interceptors:完成整个请求。

httpClient.newCall(request).enqueue(getCallback(jsonObject,

       ABConstant.REQUEST_URL_LOGOUT, handler));


从一句代码可以引申出一堆东西,累了。


我分为了四个部分,橙色第一部分实例化一个OkHttoClient类对象就可以了。


所有的逻辑大部分在拦截器Interceptors中,但进入拦截器之前还要靠分发器来调配请求任务。


分发器Dispatcher:内部维护队列和线程池,完成请求调配。


拦截器Interceptors:完成整个请求。


1)创建OkHttpClient


先创建一个OkHttpClient类的对象(httpClient)


2)创建Request


再创建一个Request类的对象,用构建器模式Bulider创建(request)


3)调用newCall


OkHttpClient类有newCall方法,需要传入一个Request类的对象的参数,也就是第二步创建出来的request。


细节


1.newCall方法的返回值是要一个实现了Call接口的类的对象,而return的是一个RealCall对象,也就是说RealCall类实现了Call接口,才能被传入参数。


2.而创建RealCall对象时候还添加了一个eventListenerFactory().create()的方法,并把自身传入参数。


3.Call接口有enqueue()异步请求方法和execute()同步请求方法,而RealCall类也有enqueue()异步请求方法和execute()同步请求方法。


4.所以回到最开始,调用Call对象的enqueue(),会动态绑定到RealCall的enqueue()方法,并执行里面的逻辑。


至此,我们完成了绿色第二部分,创建了一个Call对象。


4)调用异步请求enqueue()


在RealCall类中调用enqueue()方法,参数是CallBack,也就是要传入实现该接口的类的实例化对象,这里可以用匿名内部类,然后重写该接口中的onFailure()和onResponse()方法。


RealCall类的enqueue方法


那么在RealCall类中调用enqueue()方法执行了什么呢?


1.首先有个内置锁,锁住当前调用此方法的RealCall实例化对象。锁里面的逻辑目的就是让这个RealCall对象一辈子只能调用一次enqueue()方法或者execute()方法其中之一,也就是二者选一,便终身佛系。如果还要用enqueue()方法或者execute()方法,要用clone()方法,重新复制一份。


2.出了内置锁,会有个eventListener.callStart()监听到请求开始,dns解析开始,结束等等。如果你想知道完整请求一次需要多久时间你就注册,在OkHttpClient类中的Builder中注册。


3.主要是最后一行,会用httpClient对象调用分发器dispatcher()方法,拿到了Dispatcher类的实例化对象,再用此对象调用Dispatcher类的enqueue()方法,也就是说,RealCall类的enqueue()方法最终会调用到分发器的enqueue()方法。


4.

httpClient.dispatcher().enqueue(new AsyncCall(CallBack));

AsyncCall:


可以看做一个请求任务,把CallBack对象再次包装。AsyncCall是一个RealCall的内部类,构造参数是一个Callback对象,该CallBack对象里有实现该接口的两个方法onFailure()和onResponse()。


为什么要包装起来呢?


因为AsyncCall类实现了Runnable接口,可以有线程。


包装完CallBack,就可以进入下图了。


至此,我们完成了紫色第三部分,重写了RealCall类的enqueue()。


当然,你其实也已经完成了蓝色第四部分,你只需要new CallBack接口,然后重写里面的两个方法就好了。


你这个CallBack参数相当于一个任务,先被写到了run()方法中,然后被new了个AsyncCall包装了起来,传送给分发器的enqueue(),就是下图流程了。


f274291b0d1a4a7ba7082157fe9240d9.png


如果还想看后面就是底层了,因为RealCall类重写的enqueue()方法调用了底层的,所以就顺着这方法往下看了。


然后就顺着RealCall类重写的enqueue方法进入我们一般接触不到的分发器的enqueue()咯~~~~


5)进入底层架构——分发器


在分发器Dispatcher类中,有三个属性引人注目,也就是有三个队列,先入先出。

//readyAsyncCalls队列:(准备执行的异步请求队列)
Deque<AsyncCall> readyAsyncCalls=new ArrayDeque<>();;
//runningAsyncCalls队列:(正在执行的异步请求队列)
Deque<AsyncCall> runningAsyncCalls=new ArrayDeque<>();
//runningSyncCalls队列:(正在执行的同步请求队列)
Deque<RealCall> runningSyncCalls=new ArrayDeque<>();


为什么用ArrayDeque呢?


看数据结构去。


1.异步请求底层流程


首先在RealCall的enqueue()方法调用了Dispatcher类的enqueue()方法,并把传入enqueue()方法的参数AsyncCall对象放入到ready队列或者running队列中。


1.队列分配(线程执行前)


但是该放入哪个队列呢?


当AsyncCall任务放入正在执行的异步请求队列中,就会加入到线程池中执行。

所以————


1.条件1就是runningAsyncCalls队列里的异步请求任务不能大于最大数64。(最大数我们可以在Dispatcher类中设置set)


2.条件2就是runningCallsForHost(call),把此任务传进去,把此runningAsyncCalls队列进行遍历,判断此任务和runningAsyncCalls队列中的所有任务中同域名的最多不能超过5个,如果队列中包含5个或者5个以上一样域名,则不能加入runningAsyncCalls队列,反之则加入。(最大数我们可以在Dispatcher类中设置set)


不符合以上条件任何一个,则会放入readyAsyncCalls队列。


2.队列取出任务,执行任务(线程准备执行)


executorService().execute(call);//线程池跑任务

既然是异步请求,肯定要用子线程来实现这个请求才叫异步请求。


当放入正在执行的异步请求队列中,Dispatcher会使用它内部维护的一个线程池去调用子线程对象去跑请求任务。


execute()的参数要一个实现了Runnable接口的类的实例化对象即 包装CallBack的AsyncCall对象


而AsyncCall内部final类直接继承了NamedRunnable类,NamedRunnable类实现了Runnable接口。就等于AsyncCall内部类间接实现了Runnable接口。


所以NamedRunnable类重写了run()方法,run()方法里只有一个execute()方法,所以会执行execute()方法,但是该方法是NamedRunnable类里面的一个抽象方法,还没有方法体。


所以!!!!


在AsyncCall内部final类会重写NamedRunnable类的抽象execute()方法,也就是说execute()方法在run()方法中,而run()方法会在线程池中执行。


所以得把Callback包装成AsyncCall才能放入到底层队列以及线程池之中运行,就是因为AsyncCall间接实现了Runnable接口才能被线程池调用父类里面的run方法,然后运行execute方法时动态绑定到子类重写的方法。


3.队列移动(线程执行后)


从readyAsyncCalls队列移动到runningAsyncCalls队列的条件是什么?


在AsyncCall内部类重写的execute()方法,就是线程执行的方法。在线程每执行完一个任务后(一个任务=一个execute方法),无论请求成功或者失败,finally代码块都会执行以下语句。


finally{
    httpClient.dispatcher().finished(this);//this指当前的AsyncCall对象
}

 那么Dispatcher类的finished方法里面有什么呢?


1.首先把该任务从runningAsyncCalls队列里移除remove


2.接着执行promoteCalls方法,也就是从readyAsyncCalls队列获取任务放入runningAsyncCalls队列中


那么Dispatcher类的promoteCalls方法里面有什么呢?


这个方法其实就是从ready队列获取任务放入running队列执行


1.判断runningAsyncCalls队列的元素个数是否大于或等于最大个数,是则return。


2.判断readyAsyncCalls队列是否为空,是则return。


3.for循环:


1)遍历readyAsyncCalls队列,条件是:该任务元素和running队列中同一Host不大于最大数的情况下


2)那么该任务元素就从readyAsyncCalls队列去掉,并加入runningAsyncCalls队列,并执行下面的方法。


3)如图:

executorService().execute(call);//线程池跑任务

最后再判断runningAsyncCalls队列满了没,没满的话,反复执行For循环3个步骤,即可完成从readyAsyncCalls队列到runningAsyncCalls队列的移动,即队列移动。


2.线程池的定义


在分发器Dispatcher类中,有一个这样的方法,这个方法内部就是创建线程池的

public synchronized ExecutorService executorService(){
    if(executorService==null){
        executorService=new ThealPoolExecutor(....)
    }
    return executorService;
}


线程池和拦截器懒得写了,lay了。。



整体流程:


创建RealCall对象>>重写RealCall类enqueue()>>调用Dispatcher类enqueue()来调配任务(队列分配)>>线程池执行running队列>>线程执行完毕队列移动>>线程池执行>>队列移动。。。。。

目录
相关文章
|
缓存 Java 调度
为了更好的使用OKHttp—架构与源码分析
为了更好的使用OKHttp—架构与源码分析
260 0
为了更好的使用OKHttp—架构与源码分析
|
缓存 Java 开发者
OkHttp 3.7源码分析(一)——整体架构
OkHttp是一个处理网络请求的开源项目,是Android端最火热的轻量级框架,由移动支付Square公司贡献用于替代HttpUrlConnection和Apache HttpClient。随着OkHttp的不断成熟,越来越多的Android开发者使用OkHttp作为网络框架。
14728 0
|
29天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
2月前
|
Cloud Native Devops 云计算
云计算的未来:云原生架构与微服务的革命####
【10月更文挑战第21天】 随着企业数字化转型的加速,云原生技术正迅速成为IT行业的新宠。本文深入探讨了云原生架构的核心理念、关键技术如容器化和微服务的优势,以及如何通过这些技术实现高效、灵活且可扩展的现代应用开发。我们将揭示云原生如何重塑软件开发流程,提升业务敏捷性,并探索其对企业IT架构的深远影响。 ####
46 3
|
2月前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
28天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
154 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
30天前
|
设计模式 负载均衡 监控
探索微服务架构下的API网关设计
在微服务的大潮中,API网关如同一座桥梁,连接着服务的提供者与消费者。本文将深入探讨API网关的核心功能、设计原则及实现策略,旨在为读者揭示如何构建一个高效、可靠的API网关。通过分析API网关在微服务架构中的作用和挑战,我们将了解到,一个优秀的API网关不仅要处理服务路由、负载均衡、认证授权等基础问题,还需考虑如何提升系统的可扩展性、安全性和可维护性。文章最后将提供实用的代码示例,帮助读者更好地理解和应用API网关的设计概念。
64 8
|
2月前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
60 1
服务架构的演进:从单体到微服务的探索之旅
|
2月前
|
负载均衡 Java 持续交付
深入解析微服务架构中的服务发现与负载均衡
深入解析微服务架构中的服务发现与负载均衡
79 7
|
2月前
|
消息中间件 运维 Kubernetes
后端架构演进:从单体到微服务####
本文将探讨后端架构的演变过程,重点分析从传统的单体架构向现代微服务架构的转变。通过实际案例和理论解析,揭示这一转变背后的技术驱动力、挑战及最佳实践。文章还将讨论在采用微服务架构时需考虑的关键因素,包括服务划分、通信机制、数据管理以及部署策略,旨在为读者提供一个全面的架构转型视角。 ####
39 1

热门文章

最新文章