Vertx plus Kotlin协程永远挂起

我正在使用Kotlin协同程序重写一些Java Vertx异步代码以用于学习目的。 但是,当我尝试测试一个简单的HTTP调用时,基于协程的测试永远挂起,我真的不明白问题在哪里。 这里是一个播放器:

@RunWith(VertxUnitRunner::class) class HelloWorldTest { private val vertx: Vertx = Vertx.vertx() @Before fun setUp(context: TestContext) { // HelloWorldVerticle is a simple http server that replies "Hello, World!" to whatever call vertx.deployVerticle(HelloWorldVerticle::class.java!!.getName(), context.asyncAssertSuccess()) } // ORIGINAL ASYNC TEST HERE. IT WORKS AS EXPECTED @Test fun testAsync(context: TestContext) { val atc = context.async() vertx.createHttpClient().getNow(8080, "localhost", "/") { response -> response.handler { body -> context.assertTrue(body.toString().equals("Hello, World!")) atc.complete() } } } // First attempt, it hangs forever, the response is never called @Test fun testSync1(context: TestContext) = runBlocking<Unit> { val atc = context.async() val body = await<HttpClientResponse> { vertx.createHttpClient().getNow(8080, "localhost", "/", { response -> response.handler {it}} ) } context.assertTrue(body.toString().equals("Hello, World!")) atc.complete() } // Second attempt, it hangs forever, the response is never called @Test fun testSync2(context: TestContext) = runBlocking<Unit> { val atc = context.async() val response = await<HttpClientResponse> { vertx.createHttpClient().getNow(8080, "localhost", "/", it ) } response.handler { body -> context.assertTrue(body.toString().equals("Hello, World!")) atc.complete() } } suspend fun <T> await(callback: (Handler<T>) -> Unit) = suspendCoroutine<T> { cont -> callback(Handler { result: T -> cont.resume(result) }) } } 

每个人都能弄清楚这个问题吗?

在我看来,你的代码有几个问题:

  1. 您可以在http服务器部署之前运行测试
  2. 我相信,因为你在runBlocking执行你的代码, runBlocking你阻止了事件循环完成请求。
  3. 最后,我会建议您使用HttpClienctResponse::bodyHandler方法而不是HttpClientResponse::handler因为处理程序可能会收到部分数据。

这是一个可行的替代解决方案:

 import io.vertx.core.AbstractVerticle import io.vertx.core.Future import io.vertx.core.Handler import io.vertx.core.Vertx import io.vertx.core.buffer.Buffer import io.vertx.core.http.HttpClientResponse import kotlin.coroutines.experimental.Continuation import kotlin.coroutines.experimental.EmptyCoroutineContext import kotlin.coroutines.experimental.startCoroutine import kotlin.coroutines.experimental.suspendCoroutine inline suspend fun <T> await(crossinline callback: (Handler<T>) -> Unit) = suspendCoroutine<T> { cont -> callback(Handler { result: T -> cont.resume(result) }) } fun <T : Any> async(code: suspend () -> T) = Future.future<T>().apply { code.startCoroutine(object : Continuation<T> { override val context = EmptyCoroutineContext override fun resume(value: T) = complete() override fun resumeWithException(exception: Throwable) = fail(exception) }) } fun main(args: Array<String>) { async { val vertx: Vertx = Vertx.vertx() //0. take the current context val ctx = vertx.getOrCreateContext() //1. deploy the http server await<Unit> { cont -> vertx.deployVerticle(object : AbstractVerticle() { override fun start() { vertx.createHttpServer() .requestHandler { it.response().end("Hello World") } .listen(7777) { ctx.runOnContext { cont.handle(Unit) } } //note that it is important tp complete the handler in the correct context } }) } //2. send request val response: HttpClientResponse = await { vertx.createHttpClient().getNow(7777, "localhost", "/", it) } //3. await response val body = await<Buffer> { response.bodyHandler(it) } println("received $body") } }