协程解析前的响应,kotlin

问题描述 投票:0回答:1

你好堆栈溢出! 我正在编写一个小服务网络应用程序,它接受使用 spring boot kotlin 发送电子邮件的数据。我的问题是如何使控制器在收到请求后立即响应并在后台线程中发送实际的电子邮件。首先控制器发送电子邮件,然后在rabbitMQ上发布消息,然后才以指定的字符串形式返回响应。

@RestController
class MailController(private val emailService: EmailService, private val msgSender: CustomMessageSender) {

@PostMapping("/api/sendemail")
suspend fun sendEmail(@RequestBody request: Email): String {
    coroutineScope {
        launch(Dispatchers.IO) {
            try {
                emailService.sendMail(request.to, request.subject!!, request.msg)
                delay(2000)
                msgSender.sendMessage()
            } catch (e: Exception) {
                println(e)
            }
        }
    }
    return "email queued"
}

}

spring-boot kotlin kotlin-coroutines coroutine
1个回答
8
投票

对于这种需求,你需要了解

CoroutineScope
。 作用域限制了协程的生命周期。

当您使用

coroutineScope { ... }
时,您定义了一个将在块末尾结束的范围。这意味着,在该函数可以恢复之前,在其内部启动的所有子协程必须已完成或已取消。换句话说,
coroutineScope
将暂停当前的协程,直到所有子协程都完成为止,这不是您想要的,因为这样您将在电子邮件发送之前不会返回。

旁注:这实际上就是

coroutineScope
暂停的原因。它实际上相当于
runBlocking
的挂起,另一方面,它在等待子协程时阻塞当前线程。

你需要的是一个比你的函数体更大的范围。一种肮脏的方法是使用

GlobalScope
(它与您的应用程序具有相同的生命周期)。更好的方法是定义一个与 Spring 组件的生命周期相关联的协程作用域,这样,如果您的组件被销毁,由它启动的协程就会被取消。

执行上述操作的一种方法是定义如下范围:

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import javax.annotation.PreDestroy

@RestController
class MailController(
    private val emailService: EmailService,
    private val msgSender: CustomMessageSender
) {
    // use a regular Job() if you want all coroutines to be cancelled if one fails
    private val coroutineScope = CoroutineScope(SupervisorJob())

    // this method is NOT suspending anymore, because it doesn't call
    // suspending functions directly, it just starts a coroutine.
    @PostMapping("/api/sendemail")
    fun sendEmail(@RequestBody request: Email): String {
        coroutineScope.launch(Dispatchers.IO) {
            try {
                emailService.sendMail(request.to, request.subject!!, request.msg)
                delay(2000)
                msgSender.sendMessage()
            } catch (e: Exception) {
                println(e)
            }
        }
        return "email queued"
    }
    

    @PreDestroy
    fun cancelScope() {
        coroutineScope.cancel()
    }
}

如果您的组件始终与您的应用程序一样长,您还可以在应用程序级别定义自定义范围,并将其注入此处(无需在

@PreDestroy
中取消)。在这种情况下,
GlobalScope
仍然不是一个好主意,因为它阻止您集中更改“全局”协程的协程上下文元素。例如,拥有应用程序范围允许您添加异常处理程序、更改线程池或指定协程名称。

© www.soinside.com 2019 - 2024. All rights reserved.