来自 HttpClient 的重复请求

发布于 2024-12-28 12:53:37 字数 1590 浏览 1 评论 0原文

我在 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;
}

I am using HttpClient 4.0.1 on android... I make a POST request with a header set that is the current millis... I see that request hit the server twice within a few millis (5-10) of each other.. but the header I set is the same for both requests. This happens very sporadically... I see no real difference between the requests in wireshark... I just have no clue how this could be happening. Anyone run into this before or have any tips on how to further debug it?

here is the code I use to create the client:

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

榆西 2025-01-04 12:53:37

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

要验证/调试此问题,请尝试禁用 HTTP 重试,如下所示:

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

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

请直接使用伪代码,因为我不知道您的客户端架构的所有详细信息

处理多个连续发布

  • 禁用自动重试(如上所述)
  • 将您的 POST 请求封装在一个循环中,类似于 这个 已实施
  • 然后在手动重试之间睡眠或实施您的指数backoff

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

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

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

So what appears to be happening here is that your client sends a request, does not get a response in a timely manner, and as a result retries the same request again (as it should). This, in turn, results in multiple POST requests being sent to your server (almost in succession), which your server cannot currently deal with appropriately.

To verify/debug this, try disabling HTTP retries as follows:

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

This of course will deal with your duplicate requests issue, but then introduces another more serious issue; namely, it will try once (and only once) and fail. From the information I got from your comments, here are a few ideas you can try:

Please bare with the pseudocode as I don't have all the details of how your client is architected

Handle Multiple POSTS in Succession

  • Disable automatic retries (as above)
  • Wrap your POST requests in a loop similar to how this is implemented
  • Then either sleep between your manual retries or implement your version of exponential backoff

No matter what, your server will need the capability to handle duplicate requests in a reasonable way, this is HTTP afterall. However, you're at least giving it a chance to process the first one before it's bombarded with duplicates.

I recommend that the first step it takes when processing a request is to set some form of a (duplicate) flag. Then if/when it receives a dupe, it continues processing the first request (as usual) and silently ignores the dupes.

So just to summarize, the point of this whole scheme was to give your server a chance to set the dupe flag. After that, it's your server's job to discard (or handle) duplicate requests as needed. Does this all make sense?

半步萧音过轻尘 2025-01-04 12:53:37

我不能谈论 Android 附带的 HttpClient 版本,因为它现在实际上是基于非常旧的 Beta 版快照的分叉。但是,如果您使用的是 Apache HttpClient 4.x 的库存版本,它不会自动重试 POST 或 PUT 请求,除非进行其他配置。

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

I cannot speak for HttpClient version shipped with Android, as it is effectively a fork now based on an extremely old pre-BETA snapshot. However, if you are using a stock version of Apache HttpClient 4.x, it DOES NOT automatically retry POST or PUT requests unless configured to do otherwise.

In your particular case I suspect the HTTP message gets retransmitted by the wireless driver due to loss of connectivity or similar networking issue. HTTP is not a guaranteed delivery protocol. HTTP messages can get re-sent by lower level transports. Your application must be prepared to deal with duplicate HTTP messages.

败给现实 2025-01-04 12:53:37

重试政策我们可以

通过 VOLLEY REQUEST

进行定制

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

Retry PolicyWe can customized

WITH VOLLEY REQUEST

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文