简体   繁体   English

如何在 Ktor 流响应中正确使用 Kotlin Flow?

[英]How do I properly use Kotlin Flow in Ktor streaming responses?

emphasized text I am trying to use Kotlin Flow to process some data asynchronously and in parallel, and stream the responses to the client as they occur, as opposed to waiting until all the jobs are complete.强调文本我正在尝试使用 Kotlin Flow 来异步并行处理一些数据,并使用 stream 对客户端的响应,而不是等到所有作业完成。

After unsuccessfully trying to just send the flow itself to the response, like this: call.respond(HttpStatusCode.OK, flow.toList())在尝试将flow本身发送到响应失败后,如下所示: call.respond(HttpStatusCode.OK, flow.toList())

... I tinkered for hours trying to figure it out, and came up with the following. ...我花了几个小时试图弄清楚,并想出了以下内容。 Is this correct?这个对吗? It seems there should be a more idiomatic way of sending a Flow<MyData> as a response, like one can with a Flux<MyData> in Spring Boot.似乎应该有一种更惯用的方式来发送Flow<MyData>作为响应,就像在 Spring Boot 中使用Flux<MyData>一样。

Also, it seems that using the below method does not cancel the Flow when the HTTP request is cancelled, so how would one cancel it in Ktor?另外,当 HTTP 请求被取消时,使用以下方法似乎不会取消 Flow,那么如何在 Ktor 中取消它呢?

data class MyData(val number: Int)

class MyService {
    fun updateAllJobs(): Flow<MyData> =
        flow {
            buildList { repeat(10) { add(MyData(Random.nextInt())) } }
                // Docs recommend using `onEach` to "delay" elements.
                // However, if I delay here instead of in `map`, all elements are held
                // and emitted at once at the very end of the cumulative delay.
                // .onEach { delay(500) }
                .map {
                    // I want to emit elements in a "stream" as each is computed.
                    delay(500)
                    emit(it)
                }
        }
}

fun Route.jobRouter() {
    val service: MyService by inject() // injected with Koin

    put("/jobs") {
        val flow = service.updateAllJobs()
        // Just using the default Jackson mapper for this example.
        val mapper = jsonMapper { }

        // `respondOutputStream` seems to be the only way to send a Flow as a stream.
        call.respondOutputStream(ContentType.Application.Json, HttpStatusCode.OK) {
            flow.collect {
                println(it)
                // The data does not stream without the newline and `flush()` call.
                write((mapper.writeValueAsString(it) + "\n").toByteArray())
                flush()
            }
        }
    }
}

The best solution I was able to find (although I don't like it) is to use respondBytesWriter to write data to a response body channel.我能找到的最佳解决方案(尽管我不喜欢它)是使用respondBytesWriter将数据写入响应主体通道。 In the handler, a new job to collect the flow is launched to be able to cancel it if the channel is closed for writing (HTTP request is canceled):在处理程序中,将启动一个收集流的新作业,以便在通道关闭写入时能够取消它(HTTP 请求被取消):

fun Route.jobRouter(service: MyService) {
    put("/jobs") {
        val flow = service.updateAllJobs()
        val mapper = jsonMapper {}

        call.respondBytesWriter(contentType = ContentType.Application.Json) {
            val job = launch {
                flow.collect {
                    println(it)
                    try {
                        writeStringUtf8(mapper.writeValueAsString(it))
                        flush()
                    } catch (_: ChannelWriteException) {
                        cancel()
                    }
                }
            }

            job.join()
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Kotlin中,如何将Kovenant承诺与Elasticsearch异步响应集成在一起? - In Kotlin, how do I integrate a Kovenant promise with Elasticsearch async responses? 如何使用内容协商将json转换为ktor中的kotlin对象? - How do I convert json into a kotlin object in ktor using content negotiation? 如何为 KTOR HTTPclient Multiplatform Kotlin 项目配置引擎? 还需要添加 SSL 证书 - How do I configure the engine for KTOR HTTPclient Multiplatform Kotlin projects? Addition of SSL certificate is also required 在使用Ktor的HTML构建器时,如何在Kotlin中将部分代码提取到局部变量中? - How do I extract parts of code into local variables in Kotlin when using Ktor's HTML builder? 如何在流中使用来自另一个流数据的数据? (科特林流程) - How can I use data from another flow data in flow? (Kotlin Flow) 如何下载带有进度指示器的 Ktor 和 Kotlin 的大文件? - How can I download a large file with Ktor and Kotlin with a progress indicator? 如何通过流动对象的某些属性拆分 Kotlin 流动 - How do I split Kotlin flow by some property of flowed objects 我如何使用 Ktor 解析响应 - How do I parse a response with Ktor 如何在 ktor 中设置 socketTimeout? - How do I set the socketTimeout in ktor? 我如何在 swift Kotlin 多平台上使用 Flow? - How i can use Flow on swift Kotlin multiplatform?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM