Jetty嵌入式UberJar部署双WAR时WebSocket无升级工厂问题
没错,你遇到的问题核心就是缺少WebSocket升级工厂的配置。从日志里的No factory for Upgrade: websocket就能看出来——Jetty的连接器找不到处理WebSocket升级请求的对应工厂,导致无法将HTTP请求升级为WebSocket连接。
在嵌入式Jetty环境中,WebSocket支持不会自动为WebAppContext启用,哪怕你的WAR包里已经包含了WebSocket端点代码。尤其是当你部署多个WAR包时,需要单独为需要WebSocket功能的上下文手动配置支持。
下面分两种常见场景给出解决方案:
场景1:使用JSR-356标准WebSocket API(@ServerEndpoint注解)
如果你的WAR包是用标准的@ServerEndpoint注解实现WebSocket的,只需要在创建hubContext后添加WebSocket容器初始化配置即可:
// 为带WebSocket的hubContext启用JSR-356支持 JavaxWebSocketServletContainerInitializer.configure(hubContext, (servletContext, wsContainer) -> { // 可选:配置WebSocket容器参数,比如最大消息缓冲区大小 wsContainer.setDefaultMaxTextMessageBufferSize(1024 * 1024); wsContainer.setDefaultMaxBinaryMessageBufferSize(1024 * 1024); // 如果WAR包没有自动扫描@ServerEndpoint(比如缺少web-fragment.xml),可以手动注册端点 // wsContainer.addEndpoint(YourWebSocketEndpoint.class); });
这段代码会自动为hubContext注册WebSocket升级工厂,让Jetty能够识别并处理Upgrade: websocket请求。
场景2:使用Jetty原生WebSocket API
如果你的WAR包用的是Jetty原生的WebSocket实现(比如继承WebSocketServlet或使用WebSocketCreator),需要确保你在WAR的web.xml中配置了对应的Servlet,或者在嵌入式代码中为hubContext添加ServletHolder:
// 假设你的原生WebSocket Servlet是MyJettyWebSocketServlet ServletHolder wsServletHolder = new ServletHolder(new MyJettyWebSocketServlet()); hubContext.addServlet(wsServletHolder, "/your-websocket-path/*");
完整修改后的代码示例
public static void main(String[] args) throws Exception { Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(9191); // 显式指定端口更清晰 server.setConnectors(new Connector[]{connector}); // war #1 - 普通Web应用 WebAppContext portalContext = new WebAppContext(); portalContext.setContextPath("/portal"); portalContext.setWar("/path/to/war/file/app1.war"); // war #2 - 带WebSocket的应用 WebAppContext hubContext = new WebAppContext(); hubContext.setContextPath("/hub"); hubContext.setWar("/path/to/war/file/app2.war"); // 启用JSR-356 WebSocket支持(根据你的实际场景选择) JavaxWebSocketServletContainerInitializer.configure(hubContext, (servletContext, wsContainer) -> { wsContainer.setDefaultMaxTextMessageBufferSize(1024 * 1024); wsContainer.setDefaultMaxBinaryMessageBufferSize(1024 * 1024); }); HandlerCollection handlerCollection = new HandlerCollection(); handlerCollection.setHandlers(new Handler[]{hubContext, portalContext}); server.setHandler(handlerCollection); try { server.start(); System.out.println("Server running at http://localhost:" + connector.getPort()); System.in.read(); server.stop(); server.join(); } catch (Exception e) { e.printStackTrace(); System.exit(100); } }
额外注意:依赖检查
确保你的项目依赖中包含了Jetty WebSocket的相关库,以Maven为例:
<!-- JSR-356 API --> <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <!-- Jetty WebSocket Server实现 --> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-server</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>javax-websocket-server-impl</artifactId> <version>${jetty.version}</version> </dependency>
替换${jetty.version}为你实际使用的Jetty版本(比如9.4.50.v20221201或11.0.15)。
完成以上配置后,重启服务器,WebSocket连接应该就能正常工作了。
内容的提问来源于stack exchange,提问作者schill32

