是啥导致 okhttp 和 spdy 出现“java.io.IOException: stream was reset: CANCEL”?
Posted
技术标签:
【中文标题】是啥导致 okhttp 和 spdy 出现“java.io.IOException: stream was reset: CANCEL”?【英文标题】:What causes "java.io.IOException: stream was reset: CANCEL" with okhttp and spdy?是什么导致 okhttp 和 spdy 出现“java.io.IOException: stream was reset: CANCEL”? 【发布时间】:2014-08-03 03:19:54 【问题描述】:我正在尝试使用 OKHttp(版本 2.0.0-RC2)和 SPDY,并且在一些初步测试中看到了很多 IOException: stream was reset: CANCEL
,可能占所有请求的 10% 或更多。据我所知,在使用 Apache HttpClient 和常规 https 时,我们没有看到任何等效问题。我很确定当 SPDY 被禁用时,我们也看不到任何与 OkHttp 等效的东西 (client.setProtocols(ImmutableList.of(Protocol.HTTP_1_1))
),但我还没有做足够的测试来保证 100% 的自信。
This previous question 看到了这些异常以及忽略它们的建议,但这似乎很疯狂:我们在从服务器读取数据时遇到异常,因此我们中止了数据处理代码(使用 Jackson)。在这种情况下,我们需要做一些事情。当然,我们可以重试请求,但有时它是一个不可重试的 POST 请求,如果我们已经开始从服务器接收数据,那么服务器已经采取了请求的操作是一个不错的选择。
理想情况下,我们可以对客户端和/或服务器进行一些配置,以减少这些异常的发生率,但我对 SPDY 的了解还不够深入,甚至不知道从哪里开始寻找或建议我们的服务器管理员团队开始寻找。
堆栈跟踪,以防有帮助:
java.io.IOException: stream was reset: CANCEL
at com.squareup.okhttp.internal.spdy.SpdyStream$SpdyDataSource.checkNotClosed(SpdyStream.java:442)
at com.squareup.okhttp.internal.spdy.SpdyStream$SpdyDataSource.read(SpdyStream.java:344)
at com.squareup.okhttp.internal.http.SpdyTransport$SpdySource.read(SpdyTransport.java:273)
at okio.RealBufferedSource.exhausted(RealBufferedSource.java:60)
at okio.InflaterSource.refill(InflaterSource.java:96)
at okio.InflaterSource.read(InflaterSource.java:62)
at okio.GzipSource.read(GzipSource.java:80)
at okio.RealBufferedSource$1.read(RealBufferedSource.java:227)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.loadMore(UTF8StreamJsonParser.java:174)
at com.fasterxml.jackson.core.base.ParserBase.loadMoreGuaranteed(ParserBase.java:431)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishString2(UTF8StreamJsonParser.java:2111)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishString(UTF8StreamJsonParser.java:2092)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.getText(UTF8StreamJsonParser.java:275)
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeObject(JsonNodeDeserializer.java:205)
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeArray(JsonNodeDeserializer.java:230)
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeObject(JsonNodeDeserializer.java:202)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:58)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:15)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:2765)
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:1546)
at com.fasterxml.jackson.core.JsonParser.readValueAsTree(JsonParser.java:1363)
at (application-level code...)
【问题讨论】:
知道您正在与哪个网络服务器通信吗?有可能是我们这边或者服务器那边的协议错误。 您的请求超时设置是多少? @JesseWilson 我们正在与 CloudFlare 交谈,它使用一些 nginx 构建,请求超时为:cli.setConnectTimeout(20, TimeUnit.SECONDS); cli.setReadTimeout(15, TimeUnit.SECONDS); 【参考方案1】:有同样的问题,这是网络连接超时的结果,这是从网络服务下载大文件的结果 我将超时设置为 2 分钟,所以我将其更改为 5 分钟,它解决了我的问题
val okkHttpclient = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.MINUTES)
.writeTimeout(5, TimeUnit.MINUTES) // write timeout
.readTimeout(5, TimeUnit.MINUTES) // read timeout
.addInterceptor(networkConnectionInterceptor)
.build()
【讨论】:
这对OkHttpClient
有帮助,但仍然无法通过RestTemplate
获得它【参考方案2】:
由于 http 标头损坏,我们遇到了这个问题。默认情况下,android Base64 编码器会添加换行符,这会破坏我们的授权标头。
【讨论】:
我在 okhttp v3.11.0 中看到了这个错误,因为我没有指定我们的服务器所需的正确 API 密钥。【参考方案3】:最好的办法是在分配了 CANCEL 错误代码的两个位置设置断点:SpdyStream#closeInternal
(第 246 行)和SpdyStream#receiveRstStream
(第 304 行)。如果您可以在此处设置断点,则可以捕获 谁 正在取消您的流,这将阐明问题。
如果由于某种原因您无法附加调试器,您可以检测代码以在到达这些行时打印堆栈跟踪:
new Exception("SETTING ERROR CODE TO " + errorCode).printStackTrace();
无论哪种情况,我都是该代码的作者,我很乐意帮助您解决这个问题。
【讨论】:
...现在我想要出现 CANCEL 错误代码,我无法重现它。我会在接下来的几天里多看看,谢谢你到目前为止的帮助! 我在即将推出的 Android 项目中使用 OkHttp 2.4.0-RC1,在使用 Android M 预览模拟器 (x86) 时出现很多 CANCEL 错误。有趣的是,如果我激活调试器,大多数网络请求会突然正确通过 - 可能是竞争条件?下面是一个异常的样子:pastebin.com/AwGssiZg(我现在正在使用 Volley,但我以后可能会切换到 Retrofit) @mreichelt 同样的建议也适用。在分配了 errorCode 的代码中放置一个断点。 大家好,我很好奇您是否在这里找到了解决方案。我处于同样的情况,Cloudflare 在我的服务器前面添加 SPDY 项目(我们的后端不提供它,旧版本的 NGINX)。以上是关于是啥导致 okhttp 和 spdy 出现“java.io.IOException: stream was reset: CANCEL”?的主要内容,如果未能解决你的问题,请参考以下文章