分析基于okhttp3.10
背景
起因在于,最近应用团队找我们分析网络问题,提示SocketTimeoutException,其实很明显是无法接收到服务端的相应,此时socket连接应是正常建立的。但是对于okhttp的源码不熟悉,不清楚什么日志代表socket已经建立。
06-16 07:36:00.312 22995 23067 W System.err: java.net.SocketTimeoutException: timeout
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream S t r e a m T i m e o u t . n e w T i m e o u t E x c e p t i o n ( H t t p 2 S t r e a m . j a v a : 593 ) 06 − 1607 : 36 : 00.3142299523067 W S y s t e m . e r r : a t o k h t t p 3. i n t e r n a l . h t t p 2. H t t p 2 S t r e a m StreamTimeout.newTimeoutException(Http2Stream.java:593) 06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream StreamTimeout.newTimeoutException(Http2Stream.java:593)06−1607:36:00.3142299523067WSystem.err:atokhttp3.internal.http2.Http2StreamStreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:120)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:75)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.314 22995 23067 W System.err: at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
06-16 07:36:00.315 22995 23067 W System.err: at okhttp3.RealCall.execute(RealCall.java:69)
06-16 07:36:00.315 22995 23067 W System.err: at com.gala.sdk.player.RecordManagerImpl.doRequestWithHeader(RecordManagerImpl.java:1348)
正文
当调用者发起请求时,不管是同步请求还是异步请求,都会通过RealCall.java的execute()方法调用同一个getResponseWithInterceptorChain()
首先,在请求真正发出去之前,会现经过一些interceptor(拦截器),这其实是面向切面编程(AOP: Aspect Oriented Programming)的概念,顺序如下:
- 用户自定义拦截器
- RetryAndFollowUpInterceptor
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- CallServerInterceptor
其中ConnectInterceptor
会创建socket(也可能是从connectionPool中复用)
Interceptor介绍
Interceptor顾名思义,就是拦截器的意思。由面向切面编程(AOP: Aspect Oriented Programming)概念引入,okhttp引入AOP,旨在让用户可以简单方便的对request或者response在发送给Server前或者在返回给终端用户前做一些处理。比如,我们需要在每个请求头部加入Authorization字段(添加token校验),那么我们只需要定义一个interceptor,在每个HTTP请求发出去之前为其添加相关头部字段。
okhttp在其内部已经提供了几个Interceptor:
- RetryAndFollowUpInterceptor
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- CallServerInterceptor
下面我们对这些拦截器详细解释下。
RetryAndFollowUpInterceptor
This interceptor recovers from failures and follows redirects as necessary. It may throw an IOException if the call was canceled.
代码的注释已经非常清晰:RetryAndFollowUpInterceptor
这个拦截器主要处理请求的重试和跟踪重定向。当请求失败时(例如,由于网络问题或服务器返回非成功状态码),RetryAndFollowUpInterceptor负责决定是否应该重试请求以及如何处理重定向。
BridgeInterceptor
用于将用户的高级请求转换为底层HTTP请求。它添加了一些必要的HTTP头,例如User-Agent(用户代理),以确保请求在HTTP层面上正确发送到服务器。
通常我们使用okhttp时,只填充业务相关的一些字段,而剩下的一些HTTP Header,就在这里由okhttp进行填充。
CacheInterceptor
用于处理缓存相关的操作。它可以在请求之前检查缓存是否包含所需的响应,并在适用的情况下返回缓存的响应。如果没有缓存或需要刷新缓存,它将请求传递给下一个拦截器,以获取新的响应。
目前仅支持GET
请求的缓存,其他诸如POST/PUT/DELETE等都不支持。
缓存拦截器有个前提:客户端和服务器不能禁用缓存功能,如果禁用,这个interceptor实际上就不起作用了
另外,如果缓存过期,那么需要再次发出请求。
private CacheStrategy getCandidate() {
if (requestCaching.noCache() || hasConditions(request)) {
return new CacheStrategy(request, null);
}
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return new CacheStrategy(null, builder.build());
}
// ... ...
}
ConnectInterceptor
负责建立与服务器的连接,包括TLS握手(如果需要)。它执行DNS解析、TCP连接和TLS握手等网络操作。
CallServerInterceptor
是发送请求到服务器并获取响应的拦截器。它将请求发送到服务器,并处理服务器响应,包括响应头和响应体。如果缓存可用,它还可以缓存响应。
总结
这些拦截器在OkHttp中按照特定的顺序组成一个拦截器链,每个拦截器都负责不同的任务。这种设计允许OkHttp在处理HTTP请求和响应时实现高度的可扩展性和灵活性。每个拦截器都有其特定的责任,确保请求和响应按照期望的方式进行处理。