【问题标题】:duplicate requests from HttpClient来自 HttpClient 的重复请求
【发布时间】:2012-01-21 00:06:05
【问题描述】:

我在 android 上使用 HttpClient 4.0.1...我发出一个带有当前毫秒的标头集的 POST 请求...我看到该请求在几毫秒 (5-10) 内两次命中服务器彼此..但是我为两个请求设置的标头是相同的。这种情况很少发生......我看不出wireshark中的请求之间没有真正的区别......我只是不知道这是怎么发生的。之前有人遇到过这个问题,或者对如何进一步调试有任何提示吗?

这是我用来创建客户端的代码:

public static HttpClient getAndroidHttpClient(final int timeOut) {
    // set up the schemas
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));

    // set up our params
    HttpParams params = new BasicHttpParams();
    params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeOut);
    params.setIntParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, timeOut);
    params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeOut);
    params.setLongParameter(ConnManagerPNames.TIMEOUT, timeOut);
    params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);
    params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);

    HttpProtocolParams.setUserAgent(params, "android-client-v1.0");
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, "utf8");

    ThreadSafeClientConnManager conman = new ThreadSafeClientConnManager(params, schemeRegistry);

    DefaultHttpClient defaultHttpClient = new DefaultHttpClient(conman, params);

    return defaultHttpClient;
}

【问题讨论】:

  • 也许它来自 SchemeRegistry ?也许看看stackoverflow.com/a/1343130/320180
  • 如果禁用重试(即DefaultHttpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false))),还会出现这种情况吗?
  • Marvin,这似乎是问题所在...如果您想要赏金,请添加答案。不幸的是,现在我遇到了这个问题:stackoverflow.com/questions/2052299/… 这是有问题的,因为再次发送相同的请求会导致服务器上出现奇怪的行为,这是我首先试图解决的问题......
  • @danb 会做的 - 给我一点做更多的研究和写下来。
  • @danb 我看到你对other 帖子回复的评论:“服务器收到第一个请求并更改了一些状态.. 再次发送相同的请求真的很麻烦”。此时调整服务器如何处理重复请求是否可行?我在想:当它收到重复的请求(即时间戳定义的重复)时,以您 最初 的响应方式进行响应。您能否概述一下它是如何工作的,以便我更好地了解?

标签: android httpclient


【解决方案1】:

因此,这里发生的情况似乎是您的客户端发送了一个请求,但没有及时得到响应,因此再次重试了相同的请求(应该如此)。这反过来又会导致多个POST 请求被发送到您的服务器(几乎是连续的),而您的服务器目前无法正确处理这些请求。

要验证/调试这一点,请尝试禁用 HTTP 重试,如下所示:

defaultHttpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler
                                             (0, false));

这当然会处理您的重复请求问题,但随后会引入另一个更严重的问题;也就是说,它将尝试一次(并且仅一次)并失败。根据我从您的 cmets 获得的信息,您可以尝试以下一些想法:

请使用伪代码,因为我不知道您的客户端是如何架构的所有细节

连续处理多个帖子

  • 禁用自动重试(如上)
  • 将您的 POST 请求封装在一个类似于 this 实现方式的循环中
  • 然后在您的手动重试之间sleep 或实施您的exponential backoff 版本

无论如何,您的服务器需要以合理的方式处理重复请求的能力,这毕竟是 HTTP。但是,您至少给了它一个机会在它被重复轰炸之前处理第一个。

我建议在处理请求时采取的第一步是设置某种形式的(重复的)标志。然后如果/当它收到一个欺骗,它会继续处理 first 请求(像往常一样)并默默地忽略欺骗。

总结一下,整个方案的目的是让您的服务器有机会设置欺骗标志。之后,根据需要丢弃(或处理)重复请求是您的服务器的工作。这一切有意义吗?

【讨论】:

  • 感谢 Marvin,您的建议让我们走上了正确的道路。现在至少我们知道我们在处理什么了。
【解决方案2】:

我不能说 Android 附带的 HttpClient 版本,因为它现在实际上是一个基于极其古老的 pre-BETA 快照的分支。但是,如果您使用的是 Apache HttpClient 4.x 的库存版本,它不会自动重试 POST 或 PUT 请求,除非配置为不这样做。

在您的特定情况下,我怀疑由于连接丢失或类似的网络问题,无线驱动程序会重新传输 HTTP 消息。 HTTP 不是有保证的交付协议。 HTTP 消息可以通过较低级别的传输重新发送。您的应用程序必须准备好处理重复的 HTTP 消息。

【讨论】:

  • 我以为我遇到了一个旧的错误报告,说在 4.0 的某个地方存在一个问题,重试会影响帖子和放置..但现在我找不到它......但 Marvins 解决方案确实消除了第二个请求..所以现在我只得到“无响应”异常而不是 dup。
  • HTTP 根本不是一个真正的交付协议,它是一个应用程序协议。 HTTP 通过 TCP 传输,这是传输层。 TCP 被定义为一种可靠的传输协议,它处理重传并提供保证。我认为这就是@oleg 的回答被否决的原因。
  • @Suncat2000:通过非保证交付我的意思是 HTTP 协议没有规定在传输失败的情况下重新传输消息。比如说,服务器成功接收并处理了请求,但响应从未传递给客户端。 HTTP 将一些方法定义为非幂等的,并假设两个端点都有一个共同的策略来处理这些。
【解决方案3】:

重试策略我们可以自定义

有排球请求

stringRequest.setRetryPolicy(new DefaultRetryPolicy( 0, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); volleySingleton.addToRequestQueue(stringRequest);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多