Can somebody explain whether or not there is any use in applying coroutines to http clients like Feign if it uses synchronous http calls? Since it's blocked until it receives response, we don't get any profit (in terms of minimizing thread downtime) from writing code like this:
async {feignApi.getSomeMeaningfulData1()}
async {feignApi.getSomeMeaningfulData2()}
//And waiting for the result to combine.
do we? As I understood, it could be useful if:
1) http client used some kind of suspending function inside while reading data over the network like
suspended fun readChunkOfData()
in some loop and then the working thread wouldn't be waiting idle for the new chunk of data to come(it could work on the second request and then switch back and forth until requests are done). I don’t know much about how regular http clients work internally so I need some clarification.
2) at least it didn’t wait until the request has been processed on the other server.
Is it possible at all?
Should I use some part of Ktor framework as httpClient in Feign to resolve this issue?
If you used Feign in a GUI application, coroutines would come as great help even though Feign is a blocking API.
Coupled with a thread pool, coroutines can turn a blocking call into a suspending one from the perspective of the caller. Since blocking the GUI thread is out of the question, this is a great match. You retain the exact same sync programming model (a simple function call that returns the HTTP result) and yet you comply with the GUI thread restrictions.
Basic example with Android:
class MyActivity : AppCompatActivity(), CoroutineScope {
lateinit var masterJob: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + masterJob
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
masterJob = Job()
this.launch {
val github: GitHub = feignCall("https://api.github.com")
val contributors = github.contributors("OpenFeign", "feign")
findViewById<TextView>(R.id.about_text_view).text = contributors.toString()
}
}
override fun onDestroy() {
super.onDestroy()
masterJob.cancel()
}
suspend inline fun <reified T> feignCall(url: String) = withContext(Dispatchers.Default) {
Feign.builder().decoder(GsonDecoder()).target(T::class.java, url)
}
The same reasoning applies to any situation where you have a top-level event loop and each event handler must complete quickly in order to maintain the throughput of event handling.