【问题标题】:How to use HttpAsyncClient with multithreaded operation?如何使用 HttpAsyncClient 进行多线程操作?
【发布时间】:2012-08-11 03:24:41
【问题描述】:

与这个问题密切相关:How to use HttpClient with multithreaded operation?,我想知道 apache HttpAsyncClient 是否是线程安全的,或者它是否也需要使用 MultiThreadedHttpConnectionManager 或 ThreadSafeClientConnManager。

如果确实需要这样的连接管理器,异步库中是否存在?

我能够在异步库中找到 PoolingClientAsyncConnectionManager,但我不确定这是否是我需要的。

另外,我正在考虑使用 ThreadLocal 为每个线程创建一个 HttpAsyncClient 对象。

请注意,与我之前提到的问题不同,我需要跨会话的状态独立,即使多个会话访问同一个域也是如此。如果在会话 1 中设置了 cookie,则该 cookie 对会话 2 可见。出于这个原因,我还考虑为每个请求创建一个全新的 HttpAsyncClient 对象,尽管我得到了印象应该有更好的方法。

谢谢。

【问题讨论】:

  • 越想越觉得 ThreadLocal 行不通。使用 HttpClient,execute 方法是阻塞的,但使用 HttpAsyncClient,execute 方法是非阻塞的,因此单个线程可能会多次调用 execute,这可能会导致与多个线程在同一个 HttpClient 对象上调用 execute 相同的干扰。

标签: java multithreading apache-httpclient-4.x


【解决方案1】:

在使用和不使用 PoolingClientAsyncConnectionManager 进行负载测试后,我们发现在不使用 PoolingClientAsyncConnectionManager 时得到的结果不一致。

除其他外,我们还跟踪了我们进行的 Http 调用次数,以及已完成的 Http 调用次数(通过 cancelled(...)、completed(...) 或 failed(.. .) 相关 FutureCallback 的函数)。在没有 PoolingClientAsyncConnectionManager 的情况下,在负载过重的情况下,这两个数字有时会不匹配,这使我们相信在某个地方,某些连接正在踩踏来自其他线程的连接信息(只是猜测)。

不管怎样,使用 PoolingClientAsyncConnectionManager,数字总是匹配的,并且负载测试都成功了,所以我们肯定在使用它。

我们使用的最终代码是这样的:

public class RequestProcessor {
  private RequestProcessor instance = new RequestProcessor();
  private PoolingClientAsyncConnectionManager pcm = null;
  private HttpAsyncClient httpAsyncClient = null;
  private RequestProcessor() {
    // Initialize the PoolingClientAsyncConnectionManager, and the HttpAsyncClient 
  }
  public void process(...) {
    this.httpAsyncClient.execute(httpMethod, 
         new BasicHttpContext(), // Use a separate HttpContext for each request so information is not shared between requests
         new FutureCallback<HttpResponse>() {
      @Override
      public void cancelled() {
        // Do stuff
      }
      @Override
      public void completed(HttpResponse httpResponse) {
        // Do stuff
      }
      @Override
      public void failed(Exception e) {
        // Do stuff
      }
    });
  }
}

【讨论】:

    【解决方案2】:

    您提到“跨会话独立”。如果这只是意味着 cookie,那么我认为创建您自己的 CookieStore,当您的每个线程使用 HttpClient 时将其清除就足够了。

    我会使用ThreadLocal 创建每线程客户端,不使用共享连接管理器,然后积极清除 cookie。这个答案对清除 cookie 很有用:

    Android HttpClient persistent cookies

    类似于以下代码的内容会起作用。我已经覆盖了ThreadLocal.get() 方法来调用clear(),以防每个请求都是独立的。您也可以在 execute(...) 方法中调用 clear。

    private static final ThreadLocal<ClientContext> localHttpContext = 
        new ThreadLocal<ClientContext> () {
            @Override
            protected ClientContext initialValue() {
               return new ClientContext();
            }
            @Override
            public ClientContext get() {
               ClientContext clientContext = super.get();
               // could do this to clear the context before usage by the thread
               clientContext.clear();
               return clientContext;
            }
        };
    ...
    
    ClientContext clientContext = localHttpContext.get();
    // if this wasn't in the get method above
    // clientContext.clear();
    HttpGet httpGet = new HttpGet("http://www.google.com/");
    HttpResponse response = clientContext.execute(httpGet);
    ...
    
    private static class ClientContext {
        final HttpClient httpClient = new DefaultHttpClient();
        final CookieStore cookieStore = new BasicCookieStore();
        final HttpContext localContext = new BasicHttpContext();
        public ClientContext() {
             // bind cookie store to the local context
             localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
        }
        public HttpResponse execute(HttpUriRequest request) {
             // in case you want each execute to be indepedent
             // clientContext.clear();
             return httpClient.execute(request, httpContext);
        }
        public void clear() {
             cookieStore.clear();
        }
    }
    

    【讨论】:

    • 谢谢。这实际上看起来非常接近我已经得到的,除了清除 cookie。然而,cookie 只是一个例子。我的意思是我需要每个会话完全独立于其他会话。在另一个会话中的一个会话中根本不应该看到任何数据。我只需要担心 cookie 吗?
    • 我认为是@Tinclon。这里有一些文档似乎只在提到 HttpClient 状态时才谈论 cookie:hc.apache.org/httpcomponents-client-ga/tutorial/html/…
    • 身份验证信息是@Tinclon 需要担心的另一件事,但这只有在您自己在会话中设置身份验证用户名/密码时才需要。
    • 在这种情况下,这肯定解决了会话信息的独立性,但在尝试使用同一个客户端处理多个会话时,它仍然无助于线程之间的干扰。 (请参阅我对原始问题的评论)。
    • 为什么要使用同一个客户端@Tinclon?此外,在查看 Apache HttpClient 代码时,我看不到 execute(...) 是同步的。连接管理器是可选的吧?
    猜你喜欢
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 2019-05-08
    • 1970-01-01
    • 2018-12-15
    • 2019-02-27
    • 2018-01-01
    相关资源
    最近更新 更多