在Apache HttpClient API中,CloseableHttpClient和HttpClient有什么区别?

125
我正在研究我们公司开发的一个应用程序,它使用了Apache HttpClient库。在源代码中,它使用HttpClient类创建实例来连接服务器。
我想学习Apache HttpClient,并且已经浏览了这个示例集。所有示例都使用CloseableHttpClient而不是HttpClient。因此,我认为CloseableHttpClientHttpClient的扩展版本。如果是这样,我有两个问题:
  • 这两者之间有什么区别?
  • 哪个类建议在我的新开发中使用?

12
文档对我来说看起来很清楚:“HttpClient的基本实现,也实现了Closeable” - HttpClient是一个接口;CloseableHttpClient是一个抽象类,但因为它实现了AutoCloseable,你可以在try-with-resources语句中使用它。 - Jon Skeet
5
明白了,但关闭 HttpClient 实例有多重要呢?如果很重要,为什么 close() 方法不是基本接口的一部分呢? - Jules
4
抱歉,我不了解足够多关于HttpClient的知识来回答那个问题 :( - Jon Skeet
基本界面中不需要包含“关闭”选项,因为底层连接会自动释放回到连接管理器。 - Jeril Kuruvila
8个回答

125
  • HttpClient API的主要入口点是HttpClient接口。
  • HttpClient最基本的功能是执行HTTP方法。
  • 执行HTTP方法涉及一个或多个HTTP请求/响应交换,通常由HttpClient在内部处理。

  • CloseableHttpClient是一个抽象类,它是HttpClient的基础实现,还实现了java.io.Closeable接口。
  • 以下是其最简单形式的请求执行过程示例:

    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpGet httpget = new HttpGet("http://localhost/");
    CloseableHttpResponse response = httpclient.execute(httpget);
    try {
        //做些什么
    } finally {
        response.close();
    }

  • HttpClient资源释放: 当CloseableHttpClient实例不再需要并且即将超出范围时,必须通过调用CloseableHttpClient#close()方法来关闭与之关联的连接管理器。

    CloseableHttpClient httpclient = HttpClients.createDefault();
    try {
        //do something
    } finally {
        httpclient.close();
    }

参见参考文献以学习基础知识。


@Scadge 自Java 7以来,使用try-with-resources语句确保每个资源在语句结束时关闭。它可以用于客户端和每个响应。

try(CloseableHttpClient httpclient = HttpClients.createDefault()){

    // e.g. do this many times
    try (CloseableHttpResponse response = httpclient.execute(httpget)) {
    //do something
    }

    //do something else with httpclient here
}

67
这个回答有点类似于在自问自答。调用 HttpClient 的 close() 方法会导致哪些操作发生,如果不在适当的时间/地点调用 close() 方法是否会泄漏连接?如果过早地调用 close() 方法,是否会通过不必要的重新连接影响性能? - gilbertpilz
7
我查看了httpclient的代码以寻找答案。答案是close方法用于关闭内部状态。HTTPClient的一些实现(在httpclient库中)可以配置为使用持久资源,例如PooledHttpClientConnectionManager用于连接池,并且如果需要清理这些资源,则没有此类方法会导致麻烦。 - Deadron
我们如何在抽象类(CloseableHttpClient)上调用方法?难道我们不应该先创建一个具体的子类吗? - illcar
请查看此答案以了解概念:https://dev59.com/im855IYBdhLWcg3weUMQ#4321402 - Sagar Pudi
在我看来,主要的好处是execute方法返回CloseableHttpResponse实例,而不是HttpReponses。使用try-with-resources语句对execute调用进行管理,以确保连接返回到连接池中。 - jamie
显示剩余5条评论

34

我有同样的问题。其他答案似乎没有解释为什么close()真正必要?Op似乎也在努力找出与HttpClient等的首选工作方式。


根据Apache

// The underlying HTTP connection is still held by the response object
// to allow the response content to be streamed directly from the network socket.
// In order to ensure correct deallocation of system resources
// the user MUST call CloseableHttpResponse#close() from a finally clause.

此外,关系如下:

HttpClient (接口)

由以下实现:

CloseableHttpClient - 线程安全。

DefaultHttpClient - 线程安全但已弃用,请使用HttpClientBuilder代替。

HttpClientBuilder - 非线程安全,但可创建线程安全的CloseableHttpClient

  • 用于创建自定义CloseableHttpClient

HttpClients - 非线程安全,但可创建线程安全的CloseableHttpClient

  • 用于创建默认或最小化CloseableHttpClient

根据Apache的建议,首选方式为:

CloseableHttpClient httpclient = HttpClients.createDefault();
他们提供的示例finally子句中执行httpclient.close(),同时还使用了ResponseHandler。作为一种替代方法,mkyong的做法也很有趣:
HttpClient client = HttpClientBuilder.create().build();
他没有展示client.close()的调用,但我认为这是必要的,因为client仍然是CloseableHttpClient类的一个实例。

他没有展示client.close()的调用,但我认为这是必要的,因为client仍然是CloseableHttpClient类的一个实例。


1
HttpClientBuilder.create().build();HttpClients.createDefault(); 底层使用的,因此不应该有任何区别。 - SirHawrk

15

其他答案似乎没有解释为什么close()真的是必要的? * 2

对"HttpClient资源清理"答案的疑问。

它在旧版3.x httpcomponents doc中提到,那已经很久远了,并且与4.x HC有很大的差异。此外,解释非常简要,没有说明这个底层资源是什么。

我在4.5.2版本的源代码中进行了一些研究,发现CloseableHttpClient:close()的实现基本上只关闭其连接管理器。

(FYI)这就是当您使用共享的PoolingClientConnectionManager并调用客户端close()时,会出现异常java.lang.IllegalStateException:Connection pool shut down的原因。为避免这种情况,可以使用setConnectionManagerShared

我更喜欢在每个请求后执行CloseableHttpClient:close()

我过去在发出请求时会创建一个新的HTTP客户端实例,最后再关闭它。但是,在这种情况下,最好不要调用 close()。因为,如果连接管理器没有“共享”标志,它将被关闭,这对于单个请求来说太昂贵了。
事实上,我还发现在库clj-http中,它是Apache HC 4.5的Clojure包装器,根本不调用close()。请参见文件core.clj中的函数request

1
你能解释一下为什么使用setConnectionManagerShared并在每个请求后调用close()更好吗? - zyfo2

10

HttpClient 不是一个类,它是一个接口。你不能像你想的那样使用它进行开发。

你需要的是实现 HttpClient 接口的类,即 CloseableHttpClient


8
在下一个主要版本的库中,HttpClient接口将扩展Closeable。在那之前,建议使用CloseableHttpClient,如果不需要与早期的4.x版本(4.0、4.1和4.2)兼容性。

历史证明你错了,因为我们现在已经到了v5.1版本,而HttpClient仍然没有扩展Closeable。https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/org/apache/hc/client5/http/classic/HttpClient.html - Hakanai

2

CloseableHttpClient 是 httpclient 库的基类,所有实现都使用它。其他子类大部分已经过时。

HttpClient 是这个类和其他类的接口。

在你的代码中应该使用 CloseableHttpClient,并使用 HttpClientBuilder 创建它。如果你需要包装客户端以添加特定行为,你应该使用请求和响应拦截器,而不是用 HttpClient 包装。

此答案是针对 httpclient-4.3 的上下文给出的。


1

Jon Skeet说:

文档对我来说很清楚:“HttpClient的基本实现,同时也实现了Closeable”- HttpClient是一个接口;CloseableHttpClient是一个抽象类,但由于它实现了AutoCloseable,因此您可以在try-with-resources语句中使用它。

然后Jules问道:

@JonSkeet 那么关闭HttpClient实例有多重要?如果很重要,为什么close()方法不是基本接口的一部分?

Jules的答案

由于每次执行后底层连接都会自动释放回连接管理器,因此close不需要成为基本接口的一部分。

为了适应try-with-resources语句,必须实现Closeable。因此将其包含在CloseableHttpClient中。

注意:

AbstractHttpClient中的close方法已被弃用,它扩展了CloseableHttpClient,但我找不到该源代码。


这似乎仍然没有回答“关闭HttpClient实例有多重要?” - Vivek Chavda
1
这个答案意味着你根本不需要关闭HttpClient实例,这又意味着CloseableHttpClient只是一个错误。 - Hakanai

0

您可以像这样放置Closeables

final String uri = "http://localhost:8080/*****";
HttpGet httpGet = new HttpGet(uri);
try (final CloseableHttpClient httpClient = HttpClients.createDefault();
     final CloseableHttpResponse response = httpClient.execute(httpGet)) {
    final String result = EntityUtils.toString(response.getEntity());
    ...
} catch (Exception e) {
    ....
}

你为每个请求创建一个CloseableHttpClient实例,这不是很耗费资源吗? - Evgenia Rubanova

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接