首页 > 解决方案 > 在单例中使用 Apache CloseableHttpClient 的风险

问题描述

我正在使用 Apache Http Client 4.5.3,目前正在重构一些代码。

目前我有一个单例 Util,它有几个方法,它们的职责是通过获取、帖子、补丁等来访问 API。以前,我们一直使用 an来为每个方法的每次调用HttpClientBuilder构造一个对象。CloseableHttpClient大致来说,Singleton 的架构是这样的:

import com.google.gson.Gson
import org.apache.http.client.methods.{HttpGet, HttpPost}
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.{CloseableHttpClient, HttpClientBuilder}
import org.apache.http.util.EntityUtils
import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods.parse

object ApiCallerUtil {

  case class StoreableObj(name:String, id:Long)
  case class ResponseKey(key:Long)

  def getKeyCall(param:String): ResponseKey = {
    implicit val formats = DefaultFormats
    val get = new HttpGet("http://wwww.someUrl.com/api/?value=" + param)
    val client:CloseableHttpClient = HttpClientBuilder.create().build()
    val response = client.execute(get)
    try {
      val entity = response.getEntity
      val entityStr = EntityUtils.toString(entity)
      parse(entityStr).extract[ResponseKey]
    } finally {
      response.close()
      client.close()
    }
  }

  def postNewObjCall(param:String, i:Long): Boolean = {
    val post = new HttpPost(("http://wwww.someUrl.com/api/createNewObj"))
    val client = HttpClientBuilder.create().build()
    post.setHeader("Content-type", "application/json")
    val pollAsJson = new Gson().toJson(StoreableObj(param, i))
    post.setEntity(new StringEntity(pollAsJson))
    val response = client.execute(post)
    try {
      if (response.getStatusLine.getStatusCode < 300) true else false
    } finally {
      response.close()
      client.close()
    }
  }

  //... and so on

}

关于如何使用它的注意事项 - 我们的系统中有许多类使用这个 Singleton Util 来调用 API。这个 Singleton 将经历短时间的大量使用,其中几个类会以高频率(在几分钟内高达 @1000 次),以及在很长一段时间内多次被击中的时间段(每小时一次或两次),或者一次几个小时都不做。此外,它点击的所有 URL 都将以相同的 URL 开头(例如www.someUrl.com/api/

但我想知道在val client = HttpClientBuilder.create().build作为仅对象内可访问变量的私有 val 调用一次的地方实现它是否有意义。这样,它只会在对象实例化时创建一次。这是我暂停的地方,Apache 文档确实说了这两件事:

1.2.1。[Closeable]HttpClient 实现应该是线程安全的。建议将此类的同一个实例重复用于多个请求执行。

1.2.2。当不再需要 [Closeable]HttpClient 实例并且即将超出范围时,关闭其连接管理器以确保关闭管理器保持活动状态的所有连接并释放由这些连接分配的系统资源非常重要。

我已经阅读了大部分文档,但对以下问题没有明确的答案:

  1. 将 CloseableHttpClient 实例作为私有全局变量是否有任何风险?我担心如果某些东西过时可能会被关闭,并且我必须在一段时间后对其进行树脂验证,或者在大量使用的情况下,它会造成太多的瓶颈。根据上面的#1.2.2,变量将“永远”超出范围,因为它是一个单例对象。但是由于我只是构建客户端并在执行过程中将HttpRequest对象传递给它,而不是单独将其连接到请求之外的 API,因此这似乎无关紧要。

  2. 由于这个ApiCallerUtilSingleton 的使用方式的性质,使用它们的HttpClientConnectionManageror 是否是明智的。PoolingHttpClientConnectionManager保持稳定的连接www.someUrl.com/api/?性能提升值得吗?到目前为止,当前的实现似乎没有任何严重的性能缺陷。

感谢您的任何反馈!

标签: javascalaapache-httpclient-4.xapache-commons-httpclient

解决方案


  1. 没有(基于我 15 年以上的 HttpClient 经验)。

  2. 这实际上取决于各种因素(TLS 会话握手的开销等)。我想人们真的想确保通过持久连接执行一系列相关请求。在长时间不活动期间,您可能希望从连接池中逐出所有空闲连接。这还有一个额外的好处,那就是减少遇到陈旧(半关闭)连接问题的机会。


推荐阅读