我读到强烈建议不要使用 Globalscope
,here .
我有一个简单的用例。对于我收到的每条 kafka 消息(比如说一个 Id 列表),我必须拆分它并同时为每个 Id 调用一个休息服务,然后等待它完成并继续执行其他同步任务。该应用程序中没有其他东西需要协程。在这种情况下,我可以使用 Globalscope
吗?
注意:这不是安卓应用程序。它是一个运行在服务器端的 kafka 流处理器。它是一个在 Kubernetes 中运行的临时、无状态、容器化 (Docker) 应用程序(如果愿意,可以符合流行语)
最佳答案
您应该使用结构化并发适本地确定并发范围。如果您不这样做,您的协程可能会泄漏。在您的情况下,将它们限定为处理单个消息似乎是合适的。
这是一个例子:
/* I don't know Kafka, but let's pretend this function gets
* called when you receive a new message
*/
suspend fun onMessage(msg: Message) {
val ids: List<Int> = msg.getIds()
val jobs = ids.map { id ->
GlobalScope.launch { restService.post(id) }
}
jobs.joinAll()
}
如果对 restService.post(id)
的调用之一因异常而失败,该示例将立即重新抛出异常,并且所有尚未完成的作业都会泄漏。他们将继续执行(可能无限期地),如果他们失败了,你就不会知道了。
要解决这个问题,您需要确定协程的范围。这是没有泄漏的相同示例:
suspend fun onMessage(msg: Message) = coroutineScope {
val ids: List<Int> = msg.getIds()
ids.forEach { id ->
// launch is called on "this", which is the coroutineScope.
launch { restService.post(id) }
}
}
在这种情况下,如果对 restService.post(id)
的调用之一失败,则协程范围内的所有其他未完成的协程都将被取消。当你离开作用域时,你可以确定你没有泄露任何协程。
另外,因为 coroutineScope
会等到所有子协程完成,你可以放弃 jobs.joinAll()
调用。
旁注:
编写启动一些协程的函数时的约定是让调用者使用接收器参数决定协程范围。使用 onMessage
函数执行此操作可能如下所示:
fun CoroutineScope.onMessage(msg: Message): List<Job> {
val ids: List<Int> = msg.getIds()
return ids.map { id ->
// launch is called on "this", which is the coroutineScope.
launch { restService.post(id) }
}
}
关于kotlin - 为什么不使用 GlobalScope.launch?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54335365/