How properly call suspend function from a Controller and handle exception

My background: first time using Coroutines.

Context: my microservice needs call another rest endpoint. In other words, the Controller will consume another Rest Endpoint so a “normal” method will call a suspend function. Such suspend function will call another Rest Endpoint throw JDK11 HttpClient

Issues:
1 - I am confused if I am following properly how use coroutine
2 - I don’t know how handle exception in my case.

The code bellow is working (working doesn’t mean I am confident is well coded for production)

Service method as SUSPEND function calling another rest endpoint:

package com.tolearn.service

import com.tolearn.producer.DemoProducer
import io.reactivex.Single
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.net.http.HttpResponse.BodyHandlers
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton


@Singleton
class DemoService {


     suspend fun getCoroutine(){

        val someData = getData()

        print(someData)
    }

    suspend fun getData(): String {


        val client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .build();

        val request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:3000/employees"))
                .build();

        val response = client.sendAsync(request, BodyHandlers.ofString());
        return response.get().body() // suspend and return String not a Future
    }
}

Controller

package com.tolearn.endpoint

import com.tolearn.DemoGrpcKafkaReply
import com.tolearn.DemoGrpcKafkaRequest
import com.tolearn.DemoGrpcKafkaServiceGrpc
import com.tolearn.service.DemoService
import io.grpc.stub.StreamObserver
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.*

@Singleton
class DemoEndpoint : DemoGrpcKafkaServiceGrpc.DemoGrpcKafkaServiceImplBase(){

    @Inject
    lateinit var demoService: DemoService

    override fun send(request: DemoGrpcKafkaRequest?, responseObserver: StreamObserver<DemoGrpcKafkaReply>?) {
        println(request.toString())

        val tryingCoroutine = runBlocking {
            coroutineScope { // Creates a coroutine scope
                launch {
                    demoService.getCoroutine()
                    println("Task from nested launch")
                }
            }
        }
        println(tryingCoroutine.isCompleted)
		
		...
    }

}

Kindly, any suggestion will be appreciated.

This might help a little more than writing your own coroutines interop : grpc kotlin

And here’s the docs for coroutines: Coroutines Guide. Probably want to pay special attention to Structured Concurrency and how CoroutinScope (the interface, not coroutineScope method) acts as a lifetime manager for launched coroutine.

Just some quick feedback on what you have :
You are using runBlockingwhere you’d want to launch (so that your async api is actually async) . coroutineScope and launch paired like that isn’t much different than calling getCoroutine directly. You’d likely want to use a CoroutineExceptionHandler for capturing errors. And canceling the returned Job if the request is canceled would be good too.

1 Like

You wrote " This might help a little more than writing your own coroutines interop : grpc kotlin". Does it mean that by using “protoc-gen-grpc-kotlin” I am already taking advantage of Coroutines behind the scene? In other projects I have used “protoc-gen-grpc-kotlin” in order to expose gRPC endpoints as implementations of my proto file. Should I prefer rely on “protoc-gen-grpc-kotlin” instead of trying create a suspend function or create a Coroutine Scope? Please, if you can run your eyes on https://stackoverflow.com/questions/65348268/kotlin-coroutine-scope-is-returnrunblocking-an-issue-if-used-in-controller-en I added very detailed all my current question regard use Coroutine in Controller layer. Do you think I am in wrong direction trying to code an endpoint inside of Coroutine Scope?

Not “behind the scenes”. Read the Basics Tutorial, it has example code of generated Kotlin APIs with very obvious coroutine use (suspend methods and Flow return types).

That’s my advice. Someone already wrote a tool to generate code that does what you are asking about, might as well use it. Note: I’ve not used it myself, nor gRPC at all actually.

You just need to get a better handle on coroutines, and then it’ll probably just click. Go through the guide, read some of the documentation on the individual methods mentioned in the docs. The coroutines lead has some nice articles on Medium: https://elizarov.medium.com/. And there’s plenty of content online that will hopefully take the “magic” away: The suspend modifier — under the hood. Here’s some server side specific resources: Kotlin for server side

1 Like

I don’t think you’re actually suspending in getData(). The call to response.get() blocks the current thread, but it doesn’t suspend it (in the coroutine sense). So you’re not actually using coroutines here.

1 Like