使用 Kotlin 协程的 Spring Boot Rest 服务
Posted
技术标签:
【中文标题】使用 Kotlin 协程的 Spring Boot Rest 服务【英文标题】:Spring Boot Rest Service With Kotlin Coroutines 【发布时间】: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
不应公开挂起的功能。在内部制作协程,但对外界隐藏它们。
【参考方案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
也许你知道如何克服这个问题?
你需要提供使用的依赖和代码以上是关于使用 Kotlin 协程的 Spring Boot Rest 服务的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 Spring Boot 中使用 @Transactional 和 kotlin 协程?