问题
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