Я пишу Spring Boot Rest Service в Котлине. Я хочу использовать сопрограммы в качестве альтернативы Webflux для достижения неблокирующего асинхронного поведения. Я использую Spring Boot 2.1 и знаю, что не могу добиться истинного неблокирующего поведения, потому что я блокирую на Controller
. Тем не менее, я согласен с этим, пока Spring Boot 2.2 не станет общедоступным.
Мое приложение трехслойное, т.е. Controller-> Service-> Repository. В репозитории я вызываю другие сервисы, т.е. сетевые вызовы, и пометил метод как приостановленный.
Я хочу убедиться, что это правильный подход, кроме того, вызывает ли вызов приостановить удовольствие внутри ResourceService
блокирует вызывающегонить?
Также после прочтения Романа Елизарова https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761 Я не уверен, стоит ли мне использовать withContext
вместе со всеми моими функциями приостановки?
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)