文章目录
题目列表
- OkHttp基本实现原理是什么?
- OkHttp为什么比其他网络框架快?
- OkHttp的分发器是如何工作的?
- OkHttp的拦截器有哪些?应用拦截器和网络拦截器的区别?
- OkHttp如何复用TCP连接?
- OkHttp的连接池如何运行,一个链接能否并发请求网络?
1. OkHttp基本实现原理是什么?
OkHttp框架主要由3部分组成:Dispatcher(任务分发器)、Interceptor(拦截器)、ConnectionPool(TCP连接池)。
主要执行流程:
- 当我们发起一个请求时,请求任务会被添加到Dispatcher中。
- 在Dispatcher中会经过一些列的逻辑处理,随后会将请求传递到5个Interceptor中进行处理(Interceptor通过责任链模式进行串联)。
- 在ConnectInterceptor中会执行从ConnectionPool中获取可复用连接的逻辑。
- 最后传递给下一个拦截器CallServerInterceptor与服务端进行通信,从而获取到响应报文。
2. OkHttp为什么比其他网络框架快?
OkHttp的优势: 请求数的控制、连接池复用。
- 请求数的控制
在Dispatcher中会控制最大请求数(默认64个)和同一个主机最大请求数(默认5个)。
- 连接池复用
我们知道Http协议是应用层协议,在传输层使用的是TCP协议,而TCP协议是面相连接的,即在数据传输前后需要进行3次握手和4次挥手。当请求数量很大时,建立连接的时间损耗就会被放大。因此在OKHttp框架内部使用了连接池复用机制来避免重复建立连接。
- 请求恢复
OkHttp处理了一些网络问题,会从很多常用的连接问题中自动恢复。如果服务器配置多个IP地址,当前一个IP地址连接失败时,OkHttp会自动尝试连接下一个IP。
3. OkHttp的分发器是如何工作的?
分发器主要作用是对请求任务的分发和请求数量的控制。
线程池:
Dispatcher(分发器)中含有1个newCacheThreadPool线程池,这个线程池的特点是只要有任务来,且没有空闲线程,就会创建一条新线程来执行任务。这就会存在资源浪费的情况(如同一时刻线程数创建过多,请求数过多等问题)。
为了优化上面的问题,Dispatcher就必须控制请求的数量,避免线程池的过多创建等问题,因此引入了队列和最大请求数的限制来进行优化。
请求数限制 + 队列:
| 参数 | 含义 |
|---|---|
| maxRequests | 最大并发请求数为64 |
| maxRequestsPerHost | 每个主机最大请求数为5 |
| runningSyncCalls | 正在运行的,同步的任务集合。仅用来引用正在运行的同步任务以判断并发量。 |
| readyAsyncCalls | 异步的缓存,正在准备被消费的 |
| runningAsyncCalls | 正在运行的 异步的任务集合,仅用来引用正在运行的任务以判断并发量。 |
代码分析请参考:OkHttp(一) — OkHttp 调用流程分析
4. OkHttp的拦截器有哪些?应用拦截器和网络拦截器的区别?
| 拦截器 | 含义 |
|---|---|
| interceptors | 应用拦截器。 |
| RetryAndFollwUpInterceptor | 负责失败重连以及重定向,默认最多重试20次。 |
| BridgeInterceptor | 负责请求和响应的转换。 |
| CacheInterceptor | 负责处理缓存,默认只支持Get请求。 |
| ConnectInterceptor | 负责从链接池中复用连接。 |
| networkInterceptors | 网络拦截器。 |
| CallServerInterceptor | 负责数据传输。 |
应用拦截器和网络拦截器的区别?
调用次数:应用拦截器只会调用1次。网络拦截器可能会调用0次(从CacheInterceptor获取缓存)、1次、多次(重定向等)。
请求信息:应用拦截器只能获取原始request的信息,网络拦截器可以获取请求报文headers等信息(BridgeInterceptor中完善了请求头)。
5. ConnectionPool连接池原理?
由于HTTP是基于TCP,TCP连接时需要经过三次握手,为了加快网络访问速度,可以将请求报文首部的
Connection属性设置为Keep-Alive来实现复用连接。
OkHttp 支持5个并发Keep-Alive,默认链路生命周期为5分钟 (链路空闲后的存活时长)。连接池有ConectionPool实现,对连接进行回收和管理。
连接池的清理:
ConectionPool 在内部使用一个子线程来定时清理连接。
清理链接时分两种情况:连接池有链接、连接池没有链接。
- 连接池中有连接:执行cleanRunnable任务,内部调用 cleanup() 方法完成清理。
1. 执行cleanup() 方法清理,并返回下次需要清理的间隔时间。
2. 调用调用 wait() 方法释放ConectionPool这个类锁。
3. 等 wait 时间到了以后,重新进入 while(true) 循环进行下一次的清理,并返回下一次需要清理的时间间隔,以此循环往复。- 连接池中没有连接:cleanup()返回-1,跳出循环,下次有连接加进来时,再次开启线程进行循环清理。之所以连接池线程可以跳出循环,是因为cleanRunnable是在子线程执行。
cleanup方法执行逻辑:
- 先统计空闲连接数量。
- 然后通过for循环查找最长空闲时间的连接以及对应空闲时长。
- 然后判断这个最长空闲时间的连接是否超出
最大空闲连接数或者或者超过最大空闲时间,满足其一则清除最长空闲的连接。- 第3步如果不满足清理条件,则返回一个对应等待时间。这个对应等待的时间又分二种情况:
- 有空闲连接,则返回
keepAliveDurationNs - longestIdleDurationNs。- 没有空闲的连接,则返回
keepAliveDurationNs。
注意:清除一个空闲连接后,会返回0,再次立即开始清理。
如何判断连接是否是空闲的?
StreamAllocation创建或者复用一个Connection后,会将自己添加到Connection的connection.allocations列表中,数据读取完毕之后,会将自己从Connection的connection.allocations中移除,所以判读一个Connection是否是空闲连接可以采用引用计数法,判断connection.allocations列表中是否有StreamAllocation,如果没有就是空闲连接,否则不是。

OkHttp通过Dispatcher进行任务分发和请求数量控制,利用拦截器链处理请求,如RetryAndFollowUpInterceptor负责重试和重定向。其连接池ConnectionPool实现TCP连接复用,减少网络延迟。此外,OkHttp还支持应用拦截器和网络拦截器,分别处理不同阶段的请求和响应。

1万+

被折叠的 条评论
为什么被折叠?



