Vertx加Kotlin协程永远挂起

时间:2017-08-03 06:40:34

标签: kotlin vert.x kotlinx.coroutines

我正在使用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 个答案:

答案 0 :(得分:1)

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

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

    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")
        }
    }