Spring Boot Rest Service With Kotlin Coroutines

旧街凉风 提交于 2020-01-25 07:02:25

问题


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)

回答1:


Did you checked the new documentation on how to use co-routines ?

What's interesting for you is that class with @Controller support the following:

  • Deferred and Flow return values support in Spring WebFlux
  • Suspending function support in Spring WebFlux

So i think you could just use suspend

@RestController
class Controller(private val resourceService: ResourceService) {

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



回答2:


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).



来源:https://stackoverflow.com/questions/58558802/spring-boot-rest-service-with-kotlin-coroutines

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!