Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot Rest Service With Kotlin Coroutines

I am writing a Spring Boot Rest Service in Kotlin. I want to use coroutines as an alternative to Webflux to achieve non-blocking asynchronous behavior. I am using Spring Boot 2.1 and know that I cannot achieve true non-blocking behavior because I am blocking at Controller. However, I am okay with that for now till the time Spring Boot 2.2 is generally available.

My app is three-layered ie Controller->Service->Repository. In repository, I am calling other services ie network calls and have marked the method as suspend.

I want to ensure if this is the right approach, additionally, does call to suspend fun inside ResourceService blocks the caller thread?

Also after reading https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761 by Roman Elizarov I am not sure if I should use withContext along with all of my suspending functions?

package my.springbootapp

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import mu.KotlinLogging
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import java.util.concurrent.Executors

val logger = KotlinLogging.logger { }

@SpringBootApplication
class App

fun main(args: Array<String>) {
    SpringApplication.run(App::class.java, *args)
}

@RestController
class Controller(private val resourceService: ResourceService) {
    private val dispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()

    @GetMapping("/resource/{id}")
    fun getResource(@PathVariable("id") id: String) = runBlocking(dispatcher) {
        resourceService.get(id).also { logger.info { Thread.currentThread().name + "Returning $it" } }
    }
}

@Service
class ResourceService(private val networkResourceRepository: NetworkResourceRepository) {

    suspend fun get(id: String): Resource {
        logger.info { Thread.currentThread().name + "Getting resource" }
        return networkResourceRepository.get(id)
    }
}

@Repository
class NetworkResourceRepository {
    suspend fun get(id: String): Resource = withContext(Dispatchers.IO) {
        logger.info { Thread.currentThread().name + "Getting resource from n/w" }
        //IO operation
        Resource("resource data")
    }
}

data class Resource(val data: String)
like image 434
Amardeep Avatar asked Oct 20 '25 04:10

Amardeep


1 Answers

I think u should use coroutines with WebFlux but not instead of WebFlux. To use WebFlux with coroutines u must add WebFlux-Wrapper which makes WebFlux suspendable. WebFlux+Coroutine example By itself coroutines will not make your code non-blocking, coroutines goal is that they can just suspend. This wrappers just call some areYouFinished method on WebClient (for example), if it's not finished coroutine suspends and it will try to call same method in the future(through passing not reached code in future execution).

like image 60
lalilulelo_1986 Avatar answered Oct 23 '25 08:10

lalilulelo_1986