Служба весеннего ботинка с котлинскими сопрограммами - PullRequest
0 голосов
/ 25 октября 2019

Я пишу 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)

Ответы [ 2 ]

0 голосов
/ 25 октября 2019

Я думаю, что вы должны использовать сопрограммы с WebFlux, но не вместо WebFlux. Чтобы использовать WebFlux с сопрограммами, вы должны добавить WebFlux-Wrapper, что делает WebFlux приостановленным. Пример WebFlux + Coroutine Сами сопрограммы не сделают ваш код неблокирующим, цель сопрограмм в том, что они могут просто приостановить работу. Эти обертки просто вызывают некоторый метод areYouFinished для WebClient (например), если он не завершен, сопрограмма приостанавливается, и он попытается вызвать тот же метод в будущем (путем передачи недостигнутого кода в будущем выполнении).

0 голосов
/ 25 октября 2019

Проверяли ли вы новую документацию по как использовать сопрограммы ?

Что вас интересует, так это класс с поддержкой @Controller:

  • Поддержка отложенных и возвращаемых значений потока в Spring WebFlux
  • Поддержка приостановки функции в Spring WebFlux

Так что я думаю, вы можете просто использовать 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" } }
}
...