Vertx迁移Quarkus后首次请求成功后续报HTTP 404错误
问题现象
将基于多Verticle的Vert.x应用迁移至Quarkus后,应用启动就绪后首次HTTP请求可正常响应,但后续所有请求均返回404错误。
核心原因定位
1. 重复HttpServer实例冲突
你的代码中手动创建了一个独立的HttpServer并监听8080端口,但Quarkus默认已经启动了一个共享的Vert.x HttpServer实例监听该端口。首次请求可能被你手动创建的服务器处理,但后续请求会被Quarkus内置的HttpServer接管——而内置服务器的Router中并未配置你定义的路由规则,因此返回404。
2. Verticle生命周期管理错误
你给HttpVerticle添加了@ApplicationScoped注解,让CDI容器管理其生命周期,但Quarkus中Verticle的正确管理方式是使用@Verticle注解。这种错误的注解方式会导致Verticle初始化逻辑与Quarkus的Vert.x扩展冲突,进一步加剧路由处理的混乱。
3. 错误日志验证
从报错栈可以看到,后续请求由Quarkus的Resteasy Reactive处理链(org.jboss.resteasy.reactive.server.handlers.RestInitialHandler)接管,而非你自定义的Router,这直接证明请求未进入你配置的路由规则。
解决方向
方案1:整合到Quarkus内置Router(推荐)
移除手动创建HttpServer的逻辑,直接使用Quarkus提供的共享Router实例,让所有请求统一由内置服务器处理:
import io.quarkus.vertx.Verticle; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; import jakarta.inject.Inject; @Verticle // 替换@ApplicationScoped,让Quarkus正确管理Verticle生命周期 public class HttpVerticle extends io.vertx.core.AbstractVerticle { @Inject Router router; // 注入Quarkus共享的Router实例 @Override public void start() { // 直接在共享Router上添加路由 router.route().handler(BodyHandler.create()); router.get("/event/:eventID").handler(this::handleTrackingEvent); router.get("/cache/fill").handler(this::fillCacheWithData); router.get("/cache/purge").handler(this::purgeCache); router.get("/health/check").handler(this::checkHealth); // 无需手动创建HttpServer和监听端口,Quarkus已自动处理 } // 原有的handleTrackingEvent、fillCacheWithData等方法保持不变 }
方案2:使用Quarkus路由注解简化开发
如果不需要保留Verticle结构,可以直接用@Route注解定义路由,完全替代自定义Verticle:
import io.quarkus.vertx.web.Route; import io.quarkus.vertx.web.RoutingExchange; import io.vertx.core.eventbus.EventBus; import jakarta.inject.Inject; import org.jboss.logging.Logger; @ApplicationScoped public class HttpRoutes { @Inject EventBus eb; private static final Logger LOGGER = Logger.getLogger(HttpRoutes.class.getName()); @Route(path = "/health/check", methods = Route.HttpMethod.GET) public void checkHealth(RoutingExchange exchange) { exchange.response() .setStatusCode(200) .putHeader("content-type", "application/json") .end(); } @Route(path = "/cache/purge", methods = Route.HttpMethod.GET) public void purgeCache(RoutingExchange exchange) { eb.send(Constants.REDIS_PURGE_EVENTBUS_ADDRESS, ""); eb.send(Constants.CACHE_PURGE_EVENTBUS_ADDRESS, ""); exchange.response() .setStatusCode(200) .putHeader("content-type", "application/json") .end(); } // 其他路由方法按此格式修改 }
方案3:分离端口(不推荐)
如果必须保留手动创建的HttpServer,需修改Quarkus内置服务器的端口,避免冲突:
在application.properties中添加:
quarkus.http.port=8081
让你的自定义HttpServer继续监听8080,但这种方式会增加架构复杂度,违背Quarkus的整合设计理念。
内容的提问来源于stack exchange,提问作者Sascha Möllering

