【问题标题】:Spring Boot Rest Service With Kotlin Coroutines使用 Kotlin 协程的 Spring Boot Rest 服务
【发布时间】:2020-02-21 19:44:58
【问题描述】:

我正在用 Kotlin 编写 Spring Boot Rest Service。我想使用协程作为 Webflux 的替代方案来实现非阻塞异步行为。我正在使用 Spring Boot 2.1,并且知道我无法实现真正​​的非阻塞行为,因为我在 Controller 阻塞。不过,在 Spring Boot 2.2 普遍可用之前,我暂时可以接受。

我的应用是三层的,即Controller->Service->Repository。在存储库中,我正在调用其他服务,即网络调用,并将该方法标记为挂起。

我想确保这是否是正确的方法,此外,在ResourceService 中调用挂起乐趣会阻塞调用者线程吗?

在阅读 Roman Elizarov 的 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)

【问题讨论】:

  • “但是,在 Spring Boot 2.2 普遍可用之前,我暂时可以接受。” ----> Spring Boot 2.2 上周发布了 GA,所以试试吧。这是一种更好的体验。
  • 从您的端点中删除 runBlocking。无论如何,SpringBoot 中的每个请求都在单独的线程中处理。您的 ResourceService 不应公开挂起的功能。在内部制作协程,但对外界隐藏它们。

标签: spring-boot kotlin kotlin-coroutines


【解决方案1】:

我认为你应该使用带有 WebFlux 的协程,但不能代替 WebFlux。要将 WebFlux 与协程一起使用,您必须添加 WebFlux-Wrapper 以使 WebFlux 可暂停。 WebFlux+Coroutine example 协程本身不会使您的代码非阻塞,协程的目标是它们可以暂停。这个包装器只是在WebClient 上调用一些areYouFinished 方法(例如),如果它没有完成协程挂起,它将尝试在将来调用相同的方法(通过在未来执行中传递未到达的代码)。

【讨论】:

    【解决方案2】:

    您是否查看了how to use co-routines 上的新文档?

    你感兴趣的是带有@Controller 的类支持以下内容:

    • Spring WebFlux 中的延迟和流返回值支持
    • 在 Spring WebFlux 中暂停功能支持

    所以我认为你可以使用挂起

    @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" } }
    }
    

    【讨论】:

    • 我在创建 bean 时遇到了错误:Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Unsupported suspending handler method detected 也许你知道如何克服这个问题?
    • 你需要提供使用的依赖和代码
    猜你喜欢
    • 2020-02-18
    • 2017-03-19
    • 2016-04-29
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2015-03-17
    • 1970-01-01
    相关资源
    最近更新 更多