我得到意外结束流,而使用改造(2.9.0)与OkHttp3(4.9.1)
改装配置:
interface ApiServiceInterface {
companion object Factory{
fun create(): ApiServiceInterface {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30,TimeUnit.SECONDS)
.addInterceptor(Interceptor { chain ->
chain.request().newBuilder()
.addHeader("Connection", "close")
.addHeader("Accept-Encoding", "identity")
.build()
.let(chain::proceed)
})
.retryOnConnectionFailure(true)
.connectionPool(ConnectionPool(0, 5, TimeUnit.MINUTES))
.protocols(listOf(Protocol.HTTP_1_1))
.build()
val gson = GsonBuilder().setLenient().create()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("http://***.***.***.***:****")
.client(client)
.build()
return retrofit.create(ApiServiceInterface::class.java)
}
}
@Headers("Content-type: application/json", "Connection: close", "Accept-Encoding: identity")
@POST("/")
fun requestAsync(@Body data: JsonObject): Deferred<Response>
}
到目前为止,我发现了以下几点:
服务器端配置:
const http = require('http');
const server = http.createServer((req, res) => {
const callback = function(code, request, data) {
let result = responser(code, request, data);
res.writeHead(200, {
'Content-Type' : 'x-application/json',
'Connection': 'close',
'Content-Length': Buffer.byteLength(result)
});
res.end(result);
};
...
}
server.listen(process.env.PORT, process.env.HOSTNAME, () => {
console.log(`Server is running`);
});
因此,基于1、2、3,这不太可能是服务器端问题
基于4
、5
、6
-这不是与请求相关或与执行时间相关的格式错误问题。从7中猜测——这个问题的根源在于OkHttp,而不是改造本身。
我已经阅读了几乎一半的stackoverflow是关于解决方案的搜索,例如:意外的流结束改型关于连接错误的意外流结束改型以及Github上的OkHttp讨论:
https://github.com/square/okhttp/issues/3682
https://github.com/square/okhttp/issues/3715
但到目前为止没有任何帮助。
知道是什么导致了这个问题吗?
使现代化
我有更多关于情况的信息。
首先,我将后端的头更改为不传递内容长度,而是传递传输编码:标识。我不知道为什么,但邮递员给出了一个错误,如果theese头都存在,说这是不对的。
res.writeHead(200, {
'Content-Type' : 'x-application/json',
'Connection': 'close',
'Transfer-Encoding': 'identity'
});
在那之后,我开始在Windows托管的Android Studio模拟器上收到另一个错误(失败/成功与“意外流结束”的比率相等)
2021-12-09 14:58:19.696 401-401/? D/P2P-> FRG DEBUG:: java.io.EOFException: End of input at line 1 column 1807 path $.meta
at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1397)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:483)
at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:415)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
花了很多时间调试这个问题,我发现这个异常是由JsonReader生成的。java在方法nextNonWhitespace中尝试获取冒号、双引号和花括号或方括号,以从解码为字符数组缓冲区的json对象组成json对象。
该缓冲区本身在同一模块的fillBuffer方法中接收,其长度限制为1024个元素。在我的例子中,后端响应比这个值(1807个字符)长,所以当JsonReader。java将我的响应解析为json对象,它只需2次迭代。
每次迭代都会在此处填充缓冲区:
int total;
while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
limit += total;
// if this is the first read, consume an optional byte order mark (BOM) if it exists
if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
pos++;
lineStart++;
minimum++;
}
if (limit >= minimum) {
return true;
}
}
在响应库上调用读取方法。ktclass from
okhttp3
@Throws(IOException::class)
override fun read(cbuf: CharArray, off: Int, len: Int): Int {
if (closed) throw IOException("Stream closed")
val finalDelegate = delegate ?: InputStreamReader(
source.inputStream(),
source.readBomAsCharset(charset)).also {
delegate = it
}
return finalDelegate.read(cbuf, off, len)
}
主要问题是:
在第一次迭代中,一切都很顺利,
响应Body.kt
“读取”前1024个字符并将它们提供给JsonReader.java
,其中它构成了响应对象的一部分。
当第二次迭代到来时,响应库。kt“读取”响应的最后一部分,并用它填充char缓冲区的开头,因此char缓冲区现在包含响应的尾部作为其第一个元素,之后是第一次迭代后留在那里的所有元素。
主要问题是它在大多数情况下(大约80%)会从响应中丢失最后一个字符,大约10%的情况下会从响应中丢失2个最后一个字符,大约10%的情况下会读取所有字符。这是镜头:
在这种情况下,请求将成功。
更新2
似乎在
RealBufferedSource.kt
中获得了完整的缓冲区:
internal inline fun RealBufferedSource.commonSelect(options: Options): Int {
check(!closed) { "closed" }
while (true) {
val index = buffer.selectPrefix(options, selectTruncated = true)
when (index) {
-1 -> {
return -1
}
-2 -> {
// We need to grow the buffer. Do that, then try it all again.
if (source.read(buffer, Segment.SIZE.toLong()) == -1L) return -1
}
else -> {
// We matched a full byte string: consume it and return it.
val selectedSize = options.byteStrings[index].size
buffer.skip(selectedSize.toLong())
return index
}
}
}
}
更新3
发现这个未解决的问题,这是完全相同的行为:
Retrofit Json数据截断
另评论来自Android Studio模拟器问题跟踪器:
https://issuetracker.google.com/issues/119027639#comment9
好吧,这花了一些时间,但我已经找到了问题所在以及如何解决。
当Android Studio的模拟器在Windows系列操作系统中运行时(检查7
我发现的解决方法是添加一个拦截器来进行改进,该拦截器将检查解码到的字符串体的最后符号是否与有效json响应的符号匹配,如果错过了,则添加它们。
interface ApiServiceInterface {
companion object Factory{
fun create(): ApiServiceInterface {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val stringInterceptor = Interceptor { chain: Interceptor.Chain ->
val request = chain.request()
val response = chain.proceed(request)
val source = response.body()?.source()
source?.request(Long.MAX_VALUE)
val buffer = source?.buffer()
var responseString = buffer?.clone()?.readString(Charset.forName("UTF-8"))
if (responseString != null && responseString.length > 2) {
val lastTwo = responseString.takeLast(2)
if (lastTwo != "}}") {
val lastOne = responseString.takeLast(1)
responseString = if (lastOne != "}") {
"$responseString}}"
} else {
"$responseString}"
}
}
}
val contentType = response.body()?.contentType()
val body = ResponseBody.create(contentType, responseString ?: "")
return@Interceptor response.newBuilder().body(body).build()
}
val client = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30,TimeUnit.SECONDS)
.addInterceptor(interceptor)
.addInterceptor(stringInterceptor)
.retryOnConnectionFailure(true)
.connectionPool(ConnectionPool(0, 5, TimeUnit.MINUTES))
.protocols(listOf(Protocol.HTTP_1_1))
.build()
val gson = GsonBuilder().create()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl("http://3.124.6.203:5000")
.client(client)
.build()
return retrofit.create(ApiServiceInterface::class.java)
}
}
@Headers("Content-type: application/json", "Connection: close", "Accept-Encoding: identity")
@POST("/")
fun requestAsync(@Body data: JsonObject): Deferred<Response>
}
在此更改后,问题没有发生。
我已经按照此链接刷新了访问令牌。在将身份验证器添加到okHttp时,从改造回调onFailure方法中获取okhttp3上的意外流结束错误
我的服务接口如下所示 我像下面这样构建客户机,并将设置为90秒。 请求到达服务器,就在服务器响应时,我得到以下错误: null 它有时起作用。并不总是失败。(同一个文件总是与postman一起工作) 它始终适用于其他文件(从来没有问题)。 请求到达服务器,处理请求时没有任何错误,也会响应。在服务器完成处理并响应客户端后,我立即得到错误。 编辑1:我点击的服务器由gunicorn/20.0.4支持,
我有以下代码: 为什么它会打印Java流?
当我发现这个的时候,我已经有一段时间了。使用swankjesse提供的解决方案后,错误消失了。我只是不明白为什么这是一个解决办法。我在网上找不到任何解释这种方法解决错误的原因的东西。
问题内容: Redis 3.0.5 Spring数据Redis 1.3.6 jedis 2.6.3- 我们的Web应用程序,它通过pub / sub从redis接收数据。 -还以键/值对的形式对redis进行数据读/写。 -读/写发生在侦听器线程,独立监视线程和http请求线程上。 -我们对侦听器和Redis模板使用了相同的连接工厂 -我们的Redis服务器已配置“ timeout = 30” 在
我读过其他线程,谈到使用单线程进行读/写。但是在我们的情况下,很难使用单线程。同样,根据RedisTemplate文档,它是线程安全的。该问题是偶然的,我们无法在任何开发/测试/UAT环境中重现。因此无法找到相同的确切原因。我们做错了什么?