带有Kotlin协程的Spring Boot Rest服务

时间:2019-10-25 12:46:02

标签: spring-boot kotlin kotlin-coroutines

我正在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)

2 个答案:

答案 0 :(得分:0)

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

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

    Spring WebFlux中的
  • Deferred和Flow返回值支持
  • Spring WebFlux中的暂停功能支持

所以我认为您可以使用暂停

final Reader in = new FileReader("counties.csv");
final Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(in);

for (final CSVRecord record : records) { // Simply iterate over the records via foreach loop. All the parsing is handler for you
    String populationString = record.get(7); // Indexes are zero-based
    String populationString = record.get("population"); // Or, if your file has headers, you can just use them

    … // Do whatever you want with the population
}

答案 1 :(得分:0)

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