OkHttp3 超时设置

OkHttp3 超时设置,第1张

目录

1,AsyncTimeout

2-1,耗时段介绍「从上往下」

2-2,4个超时设置「OkHttpClient.Builder」

2-3,耗时 *** 作之间的关联

0,参考

okhttp Timeout 超时设置与用法解释: https://www.jianshu.com/p/7547a5e8524a

1,AsyncTimeout

AsyncTimeout.enter 案例

1,首次创建 Watchdog + AsyncTimeout作为static量,避免重复创建

2,单线程,设置超时,通过pine/pip机制,若超时通过写入流方式唤醒

2-1,耗时段介绍「从上往下」

耗时 *** 作调用位置

DNS解析 「ConnectInterceptor」streamAllocation.newStream

「RouteSelector」address.dns().lookup()

连接时间「ConnectInterceptor」streamAllocation.newStream

「Platform」socket.connect()

写入request 「CallServerInterceptor」httpCodec.writeRequestHeaders

服务器响应 「ConnectInterceptor」streamAllocation.newStream

「RealConnection」socket.connect()

读取response 「CallServerInterceptor」httpCodec.readResponseHeaders

2-2,4个超时设置「OkHttpClient.Builder」

api 简介 生效机制

callTimeout() 整个流程耗费的超时时间 RealCall.execute方法,设置进入

AsyncTimeout + WatchDog实现

connectTimeout()三次握手 + SSL建立耗时 socket.connect(address, connectTimeout)

readTimeout() source读取耗时 source.timeout(readTimeout)

AsyncTimeout + WatchDog实现

rawSocket读取耗时 rawSocket.setSoTimeout(readTimeout)

writeTimeout() sink写入耗时sink.timeout(writeTimeout)

AsyncTimeout + WatchDog实现

重点说一下,callTimeout这个参数,网络上很少人使用。这个就是整个网络流程的超时设置。

2-3,耗时 *** 作之间的关联

route.requiresTunnel() callTimeout = dns + connection + readTimeout + readTimeout + writeTimeout + 其它

无 callTimeout = dns + connectTime + readTimeout + 其它

1,基本耗时:dns + 三次握手耗时 + 服务器响应耗时

2,若有渠道,则增加 source.timeout().(readTimeout) + sink.timeout.(writeTimeout)

Okhttp的浅层架构分析

Okhttp的责任链模式和拦截器分析

Okhttp之RetryAndFollowUpInterceptor拦截器分析

Okhttp之BridgeInterceptor拦截器分析

Okhttp之CacheInterceptor拦截器分析

Okhttp之ConnectInterceptor拦截器分析

Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation

Okhttp之CallServerInterceptor拦截器分析

浅析okio的架构和源码实现

HTTP 1.1 默认启用长TCP连接,但所有的请求-响应都是按序进行的(这里的长连接可理解成半双工协议。即便是HTTP1.1引入了管道机制,也是如此)。复用同一个TCP连接期间,即便是通过管道同时发送了多个请求,服务端也是按请求的顺序依次给出响应的;而客户端在未收到之前所发出所有请求的响应之前,将会阻塞后面的请求(排队等待),这称为"队头堵塞"(Head-of-line blocking)。

HTTP2.0复用TCP连接则不同,虽然依然遵循请求-响应模式,但客户端发送多个请求和服务端给出多个响应的顺序不受限制,这样既避免了"队头堵塞",又能更快获取响应。在复用同一个TCP连接时,服务器同时(或先后)收到了A、B两个请求,先回应A请求,但由于处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。HTTP2.0长连接可以理解成全双工的协议。

HTTP2.0 使用 多路复用 的技术,多个 stream 可以共用一个 socket 连接。每个 tcp连接都是通过一个 socket 来完成的,socket 对应一个 host 和 port,如果有多个stream(即多个 Request) 都是连接在一个 host 和 port上,那么它们就可以共同使用同一个 socket ,这样做的好处就是 可以减少TCP的一个三次握手的时间。

在OKHttp里面,负责连接的是 RealConnection 。

RealConnection是Connection的实现类,代表着链接socket的链路,如果拥有了一个RealConnection就代表了我们已经跟服务器有了一条通信链路。与服务的三次握手也是在这里实现的。下面看看它的属性和构造函数。

下面看看核心方法connect():

connectTunnel()隧道链接

最终还是要调用到connectSocket():

1、创建Socket,非SOCKS代理的情况下,通过SocketFactory创建;在SOCKS代理则传入proxy手动new一个出来。

2、为Socket设置超时

3、完成特定于平台的连接建立

4、创建用于I/O的source和sink

至于代理的相关逻辑,这里暂时就不深究了,后续会再单独去了解。

继续看http2Connection.start()方法:

看看这个readerRunnable

从Realconnection调用connect()创建了socket连接之后(这里讨论走http2.0协议分支),创建了一个http2Connection 对象,启用了一个readerRunnable的线程,run()方法的主要工作是循环地执行reader.nextFrame()方法。

看看reader.nextFrame()干了啥:

再来重点看看 readHeaders(handler, length, flags, streamId)方法,因为在后面的CallServerInterceptor拦截器会追踪到,提前了解一下,是怎么读取response的headers的:

链接池,看名字就能联想到线程池之类的池设计,都是为了减少资源创建,提高资源复用率而设计的。连接池是用来管理http和http/2的链接复用,通过让同一个address将共享同一个connection,以便减少网络请求延迟。

成员变量和构造函数:

先搞明白那个清除cleanup(long now)方法:

pruneAndGetAllocationCount()方法:

再来看看清理的任务是什么时候执行的:

再来看看ConnectionPool的其他方法,看明白了这些方法也就大概了解了它的工作流程,get()方法:

connectionBecameIdle()方法:

deduplicate()方法:

evictAll()方法:

ConnectionPool的主要职责就是维护了一个RealConnection的双端队列,并且维护了一个定时清理空闲和多余connection的线程池,并提供了一些相应的 *** 作方法来维护连接池的稳定性和提供相应的功能。

流分配,Connection是建立在Socket之上的物理通信信道,而Stream则是代表逻辑的流,至于Call是对一次请求过程的封装。之前也说过一个Call可能会涉及多个流(比如重定向或者auth认证等情况)。所以我们想一下,如果StreamAllocation要想解决上述问题,需要两个步骤,一是寻找连接,二是获取流。所以StreamAllocation里面应该包含一个Stream;还应该包含连接Connection。如果想找到合适的链接,还需要一个连接池ConnectionPool属性。所以应该有一个获取流的方法在StreamAllocation里面,还应该有完成请求任务的之后的方法来关闭流对象,还有终止和取消等方法,以及释放资源的方法。

成员变量及构造函数:

看到这些成员变量是不是很眼熟,就是之前讲过的链接以及连接池,路由这些,下面看看它的几个重要的方法,先看看在ConnectInterceptor里调用到的newStream()方法:

findHealthyConnection()方法

继续看下findConnection()方法:

1、循环获取connection实例,直到获取到健康可用的,获取实例的时候先找是否有已经存在的连接,如果有已经存在的连接,并且可以使用(!noNewStreams)则直接返回。

2、没有现成的就根据已知的address在connectionPool里面找,如果有连接,则返回

3、更换路由,更换线路,在connectionPool里面再次查找,如果有则返回。

4、如果以上条件都不满足则直接new一个RealConnection出来

5、new出来的RealConnection通过acquire关联到connection.allocations上

6、做去重判断,如果有重复的socket则关闭

其他方法暂时没用到,不做一一讲解,下篇分析最后一个拦截器CallServerInterceptor,最终跟服务器产生通信的阶段,结合这个拦截器再来重新组织起来看看这篇文章讲到的知识点。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/11308354.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存