Vert.x调用API时出现Response head already sent异常的排查与解决
Vert.x路由链调用异常问题解决
问题背景
我是Vert.x新手,正在搭建服务器和路由API,调用接口时触发java.lang.IllegalStateException: Response head already sent异常,相关代码、异常信息如下:
代码示例
package com.yashkirat.zee.vertx; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; public class Main { public static void main(String[] args) { Vertx vertx=Vertx.vertx(); Router router=Router.router(vertx); Route handler1=router.route("/hello") .handler(routingContext->{ HttpServerResponse response=routingContext.response(); response.setChunked(true); response.write("Hi Human, This is hello1"); routingContext.vertx() .setTimer(5000, tid -> routingContext.next()); }); Route handler2=router.route("/hello") .handler(routingContext->{ HttpServerResponse response=routingContext.response(); response.write("Hi Human, This is hello2"); routingContext.vertx() .setTimer(5000, tid -> routingContext.next()); }); Route handler3=router.route("/hello ") .handler(routingContext->{ HttpServerResponse response=routingContext.response(); response.write("Hi Human, This is hello3"); response.end(); }); HttpServer httpServer=vertx.createHttpServer(); httpServer.requestHandler(router).listen(8091); } }
异常信息
SEVERE: Unhandled exception java.lang.IllegalStateException: Response head already sent at io.vertx.core.http.impl.Http1xServerResponse.checkHeadWritten(Http1xServerResponse.java:709) at io.vertx.core.http.impl.Http1xServerResponse.setStatusCode(Http1xServerResponse.java:149) at io.vertx.ext.web.impl.RoutingContextImpl.checkHandleNoMatch(RoutingContextImpl.java:156) at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:142) at com.yashkirat.zee.vertx.Main.lambda$null$2(Main.java:49) at io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:948) at io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:919) at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:55) at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:158) at io.vertx.core.impl.ContextInternal.emit(ContextInternal.java:194) at io.vertx.core.impl.VertxImpl$InternalTimerHandler.run(VertxImpl.java:937) at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98) at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:153) at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:750)
尝试过的修复方法
- 在所有包含
routingContext.vertx().setTimer(5000, tid -> routingContext.next());的位置添加try-catch; - 移除所有
response.write("Hi Human, This is hello2");语句,只保留倒数第二个路由中的该语句。
以上两种方法均未解决问题。
问题原因
- 路由路径不匹配:handler3的路由是
"/hello "(末尾多了一个空格),和请求的/hello路径不匹配。当handler2调用routingContext.next()后,Vert.x找不到后续匹配的路由,会自动尝试返回404状态码,但此时响应头已经因之前的response.write()操作被发送,无法再修改状态码,触发异常。 - 响应生命周期冲突:调用
response.write()时,即使开启了chunked模式,Vert.x也会自动发送响应头。后续Vert.x尝试设置404状态码时,发现响应头已发送,抛出Response head already sent异常。
修复方案
步骤1:修正路由路径
将handler3的路由从"/hello "改为"/hello",确保三个路由都匹配同一个请求路径,让routingContext.next()能正确找到下一个处理器。
步骤2:完善响应头设置(可选但建议)
在第一个handler中添加content-type响应头,避免客户端解析响应内容时出现问题。
修正后的完整代码
package com.yashkirat.zee.vertx; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; public class Main { public static void main(String[] args) { Vertx vertx = Vertx.vertx(); Router router = Router.router(vertx); Route handler1 = router.route("/hello") .handler(routingContext -> { HttpServerResponse response = routingContext.response(); response.setChunked(true); response.putHeader("content-type", "text/plain"); // 添加内容类型头 response.write("Hi Human, This is hello1\n"); routingContext.vertx() .setTimer(5000, tid -> routingContext.next()); }); Route handler2 = router.route("/hello") .handler(routingContext -> { HttpServerResponse response = routingContext.response(); response.write("Hi Human, This is hello2\n"); routingContext.vertx() .setTimer(5000, tid -> routingContext.next()); }); Route handler3 = router.route("/hello") // 移除末尾空格 .handler(routingContext -> { HttpServerResponse response = routingContext.response(); response.write("Hi Human, This is hello3\n"); response.end(); // 结束响应 }); HttpServer httpServer = vertx.createHttpServer(); httpServer.requestHandler(router).listen(8091); } }
注意事项
- 确保请求链最终能走到调用
response.end()的逻辑,不要让请求无疾而终,否则Vert.x会自动尝试返回404,引发响应头冲突。 - 开启chunked模式后,所有
write操作会流式发送,最终必须调用end()来结束响应。
内容的提问来源于stack exchange,提问作者Weirdoooo
相关产品推荐
相关产品推荐

