I'm using Apache Http Client 4.5.3 and currently refactoring some code.
Currently I have a singleton Util that has several methods whose responsibility is to hit an API with gets, posts, patches, etc. Previously, we had been using an HttpClientBuilder
to construct a CloseableHttpClient
object for every call of every method. Roughly, the architecture for the Singleton is something like this:
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
}
Notes about how this is used - we have many classes all over our system that use this Singleton Util to make calls to the API. This Singleton will go through short periods of heavy use where several classes will hit the same calls with heavy frequency (up to @1000 of times within several minute periods), and also periods where it is hit several times over a long period of time (once or twice an hour), or not at all for hours at a time. Also, all the URLs it hits will start with the same URL (e.g. www.someUrl.com/api/
)
But I'm wondering if it would make sense to implement it where the val client = HttpClientBuilder.create().build
is called once as a private val for a in-object-only accessible variable. This way it is only created once, upon instantiation of the object. Here's where I pause, the Apache documentation does say these two things:
1.2.1. [Closeable]HttpClient implementations are expected to be thread safe. It is recommended that the same instance of this class is reused for multiple request executions.
1.2.2. When an [Closeable]HttpClient instance is no longer needed and is about to go out of scope it is important to shut down its connection manager to ensure that all connections kept alive by the manager get closed and system resources allocated by those connections are released.
I've read through most of the documentation, but don't have a solid answer to the following:
Are there any risks of having the CloseableHttpClient instance as private global variable? I'm worried something may get shut down if it's stale and that I'd have to resinstantiate it after a period of time, or, in the cases of heavy use, it would create too much of a bottleneck. Per the #1.2.2 above, the variable will "never" go out of scope, since it's a singleton object. But since I'm building just the client and passing it the
HttpRequest
objects as I go, and not connecting it to the API outside of the request alone, it seems it shouldn't matter.Due to the nature of how this
ApiCallerUtil
Singleton is used, would it be wise to perhaps make use of theirHttpClientConnectionManager
or.PoolingHttpClientConnectionManager
to maintain a steady connection towww.someUrl.com/api/
? Will the performance increase be worth it? So far, the current implementation doesn't seem to have any drastic performance drawbacks.
Thanks for any feedback!