OkHttp浅析
最近在看HTTP相关内容时,突然想到OkHttp是支持HTTP2.0的,所以特地看了下OkHttp的源码,并以此记录。
OkHttp VS Volley
这两个库都是项目用的比较多的
OkHttp有什么优势?
- 支持HTTP2/SPDY
- socket自动选择最好的路线,并支持自动重连
- 拥有自动维护的socket连接池,减少握手次数
- 拥有队列线程池,轻松写并发
- 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
- 基于Headers的缓存策略
OkHttp能竞争过其他库的核心功能是什么?
okhttp使用Dispatcher进行线程分发,它有两种方法,一个是普通的同步单线程;另一种是使用了队列进行并发任务的分发(Dispatch)与回调,这也是okhttp能够竞争过其它库的核心功能之一
OkHttp请求流程是什么样的?
总体分为两种:一种同步请求,一种异步请求
大体流程如下:
同步请求
异步请求
OkHttp中Intercept是什么?
Intecept基于责任链模式实现,是一个监视、重写、重试请求的强有力机制。拦截器可以串联。
拦截器分为两类:
- 应用拦截器,在发送请求之前和获取响应之后进行操作的。通过addInterceptor来进行添加。
- 网络拦截器,在进行网络获取前进行操作的。通过addNetworkInterceptor进行添加。
拦截器执行顺序:
应用拦截器和网络拦截器各有什么优势?
应用拦截器
- 不需要考虑中间状态的响应,比如重定向或者重试
- 应用拦截器只会调用一次,即便数据来源于缓存
- 只考虑应用的初始意图,不考虑OkHttp注入的Header,比如:if-None-Match,意思就是不管其他外在因素,只考虑最终的返回结果
- 自定义的应用拦截器是第一个开始的执行的拦截器,所以可以决定是否执行其他的拦截器,通过Chain.proceed()方法。
- 允许重试和发送多条请求,调用Chain.proceed()方法
网络拦截器
- 网络拦截器可以操作重定向和失败重连的返回值
- 我们可以看出,取缓存中的数据不会去进行网络请求,也就不会执行Chain.proceed()。
- 意思是通过网络拦截器可以观察到所有通过网络传输的数据
- 请求服务连接的拦截器先于网络拦截器执行,在进行网络拦截器的时候,可以看到Request中服务器的请求连接信息。
OkHttp是怎么复用连接池的?
OkHttp支持5个并发的KeepAlive,默认链路生命为5分钟(链路空闲后,保持存活的时间)
StreamAllocation是计数对象,通过引用计数来管理socket,专门用于淘汰末位的Socket,淘汰策略如下:
- 并发socket空闲连接数超过5个
- 某个socket的keepalive的时间大于五分钟
Connection自动回收是怎么实现的?
这段死循环是一个阻塞的清理任务,首先进行清理,返回下次需要清理的间隔时间,当等待时间到了之后,再次进行清理。
1 | //Socket清理的Runnable,每当put操作时,就会被主动调用 |
Connection的清除逻辑是什么样的?
1 | long cleanup(long now) { |
清除逻辑:
- 遍历Deque中所有的RealConnection,标记泄漏的连接
- 如果被标记的连接满足(空闲socket连接超过5个&&keepalive时间大于5分钟),就将此连接从Deque中移除,并关闭连接,返回0,也就是将要执行wait(0),提醒立刻再次扫描
- 如果(目前还可以塞得下5个连接,但是有可能泄漏的连接(即空闲时间即将达到5分钟)),就返回此连接即将到期的剩余时间,供下次清理
- 如果(全部都是活跃的连接),就返回默认的keep-alive时间,也就是5分钟后再执行清理
- 如果(没有任何连接),就返回-1,跳出清理的死循环
如何标记并找到最不活跃的连接?
1 | //类似于引用计数法,如果引用全部为空,返回立刻清理 |
- 遍历Deque中所有的RealConnection,标记泄漏的连接
- 如果被标记的连接满足(空闲socket连接超过5个&&keepalive时间大于5分钟),就将此连接从Deque中移除,并关闭连接,返回0,也就是将要执行wait(0),提醒立刻再次扫描
- 如果(目前还可以塞得下5个连接,但是有可能泄漏的连接(即空闲时间即将达到5分钟)),就返回此连接即将到期的剩余时间,供下次清理
- 如果(全部都是活跃的连接),就返回默认的keep-alive时间,也就是5分钟后再执行清理
- 如果(没有任何连接),就返回-1,跳出清理的死循环