Android(Java)HttpURLConnection静默重试'读'超时 [英] Android (Java) HttpURLConnection silent retry on 'read' timeout

查看:327
本文介绍了Android(Java)HttpURLConnection静默重试'读'超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我使用 Google Volley 来获取HTTP请求,它主要使用 Java HttpURLConnection

So I'm using Google Volley for HTTP request, which basically uses Java's HttpURLConnection.

根据我的测试,问题是这个

HttpURLConnection 上的'read'超时达到时,在连接关闭之前执行静默重试并抛出相关异常( SocketTimeoutException )。

According to my tests, the problem is this:
When the 'read' timeout on the HttpURLConnection reaches, a silent retry is executed before the connection is closed and the relevant exception thrown (SocketTimeoutException).

注意
- 使用 HTTP POST 请求。

- '读'超时与'连接'超时不同。

- 如果'读'超时(通过调用设置) connection.setReadTimeout(int))未设置(0),或设置为大于 connection.setConnectTimeout(int),不会发生此错误。

- 此问题已经讨论过,这里例如,但我没有找到任何令人满意的解决方案。

- 可以找到一个有点相关的问题这里,但我不确定它是否相关(是吗?)

Note that:
- I noticed this error when using HTTP POST request.
- 'read' timeout is different than 'connect' timeout.
- If the 'read' timeout (set by calling connection.setReadTimeout(int)) is NOT set (0), or set to a greater value than connection.setConnectTimeout(int), this error does not occur.
- This issue has been discussed, here for example, but I haven't found any satisfying solution.
- A somewhat related issue can be found here, but I'm not sure it is relevant (is it?)

更多背景

我的应用程序用于付款,因此不重试请求是至关重要的(是的,我知道它可以由服务器处理,我希望我的客户端正确 无论如何。

More Background
My app is used for paying money, so not retrying a request is crucial (yes, I know it can be handled by the server, I want my client to be "correct" anyway).

当设置'read'超时时,如果服务器连接已建立,但服务器等待/ sleeps / delays-response'timeout'在回答之前的时间(因此提高'读'异常而不是'连接'异常),在异常引发之前发送另一个(静默)请求,导致2个类似的请求,这是不可接受的。

When the 'read' timeout is set, in case the server connection is established, but the server waits/sleeps/delays-response that 'timeout' time before answering (thus raising the 'read' exception and not the 'connect' exception), another (silent) request is sent just before that exception is raised, resulting in 2 similar requests, which is not acceptable.

我在寻找什么样的解决方案?

嗯,一个能很好地解决这个问题/错误的解决方案,就像解释这里一样(但我再次,我认为在这种情况下它是无关紧要的。)
另外,我希望保持原始流程不变,这意味着不要强制连接关闭或类似的东西。

What kind of solution am I looking for?
Well, a one that will nicely solve this problem/bug, just like the fix explained here (but I again, I think it's irrelevant in this case).
Also, I would want to keep the original flow as is, meaning not forcing the connection to close or anything like that.

我现在要做的是,将'read'超时设置为'connection'超时的两倍(它们同时开始计数),制作确保首先引发'连接'异常。我还将尝试在服务器端克服此问题。问题是,这个读取超时是有原因的,而我当前的实现实际上只是忽略它,并且只处理连接超时。

What I'm gonna do for now, is set the 'read' timeout to twice the 'connection' timeout (they start counting at the same time), to make sure the 'connection' exception is raised first. I will also try to overcome this issue on the server side. The problem is, this 'read' timeout is there for a reason, and my current implementation practically just ignores it, and handles only 'connection' timeouts.

编辑
排球图书馆 RetryPolicy 对此问题没有影响,因为这是静默重试。
我在图书馆里看起来尽可能深。到处记录/断点,取消了重试的调用。我怎么知道它是99.99% HttpURLConnection 问题。

EDIT The Volley library's RetryPolicy has not affect on this issue, as this is a silent retry. I looked as deep as possible inside the library. Logs/breakpoints everywhere, cancelled the calls to retry. That how I know it is 99.99% a HttpURLConnection issue.

推荐答案

这个错误的决定是由开发人员在2006年做出的。
这是一个很好的引用,从java的角度来解释整个情况:

This bad decision was made by a developer in the year 2006. Here is a nice citation from someone that explains the whole situation from java perspective:


正如你可能猜到的那样,这是一个错误( http: //bugs.java.com/bugdatabase/view_bug.do?bug_id=6382788 )。不是重试机制,当然,这只是废话。错误是它也发生在POST(默认情况下)但不要担心,Bill很久以前就修复了这个bug。比尔通过引入一个切换来修复它.Bill了解了向后兼容性。比尔认为最好在默认情况下保持切换开启这会让它向后兼容。比尔微笑。他已经可以看到惊讶的开发者面孔了围绕全球跑进这个。请不要像比尔?

来源

"As you probably guessed by now it's a bug (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6382788). Not the retry mechanism, of course, that's just crap. The bug is that it also happens for a POST (which is by default not idempotent per HTTP RFC). But don't worry, Bill fixed that bug a long time ago. Bill fixed it by introducing a toggle. Bill learned about backward compatibility. Bill decided it's better to leave the toggle 'on' by default because that would make it bug-backward-compatible. Bill smiles. He can already see the faces of surprised developers around the globe running into this. Please don't be like Bill?"
Source

建议的解决方案是:

System.setProperty("sun.net.http.retryPost", "false")

但是我们不能在android上做到这一点!唯一剩下的解决方案是:

But we cannot do this on android! The only remaining solution then is:

httpURLConnection.setChunkedStreamingMode(0);

这似乎有效,但在请求时间的perspektive中效果不是很好。

编辑:
我无法使用这个实现,所以我找了一个替代库。
我发现HttpUrlConnection的实现使用 OkHttp ,因为Android 4.4。由于OkHttp是开源我可以searc如果他们也有静默重试的问题。他们遇到问题并于2016年4月修复。 CommonsWare (一个真正的 android expert )解释说每个制造商都可以决定他们可以使用哪种实现。因此,必须有很多设备在POST请求上进行静默重试,作为开发人员,我们只能尝试一些workarrounds。

I could not use this implementation so i looked for an alternative library. I found out that the implementation of HttpUrlConnection uses OkHttp since Android 4.4. As OkHttp is opensource i could search if they have also problems with silent retries. And yep they had problems with it and fixed it in April, 2016. CommonsWare (a real android expert) explains that every manufacturer can decide which implementation they can use. As a consequence of this there must be a lot of devices out there that do silent retries on POST requests and as a developer we only can try some workarrounds.

我的解决方案是更换库

编辑2:给你一个最终答案:您现在可以使用OkHttp作为Volley的传输层,只需最少的代码。

EDIT 2: To give you a final answer: You can now use OkHttp as the transport layer for Volley with minimal code.

另一个有用的解决方案

这篇关于Android(Java)HttpURLConnection静默重试'读'超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆