Jersey Java服务器SSE连接始终处于Pending状态问题求助
看起来你遇到的SSE连接pending问题,和你发现的SseEventSink核心字段(requestScope、requestContext等)未填充直接相关——这大概率是请求上下文没有正确绑定到SSE处理流程导致的。结合Jersey 2.28的特性,给你几个针对性的排查和解决方向:
检查请求上下文的绑定范围
Jersey的SseEventSink完全依赖请求作用域(RequestScope)来初始化核心字段。如果你的service.subscribe(id, sink, sse)逻辑是在原请求线程外执行(比如异步线程池),就会导致上下文丢失,字段无法填充。
解决办法:确保订阅逻辑在原请求线程中初始化,或者手动将请求上下文绑定到异步线程。如果是Jersey原生环境,可以用RequestScopeAPI强制绑定:import org.glassfish.jersey.process.internal.RequestScope; import org.glassfish.jersey.process.internal.RequestScopeProvider; // 在资源方法中获取请求作用域 RequestScope requestScope = RequestScopeProvider.getInstance().getRequestScope(); // 用runInScope包裹异步逻辑 requestScope.runInScope(Collections.emptyMap(), () -> { service.subscribe(id, sink, sse); });验证SSE依赖与容器配置的完整性
虽然你引入了jersey-media-sse,但要确认是否搭配了正确的容器适配依赖,且开启了异步支持:- Maven依赖要确保包含容器核心包:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-sse</artifactId> <version>2.28</version> </dependency> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> <version>2.28</version> </dependency> - 在Jersey的
Application类中注册SseFeature并开启异步:@ApplicationPath("/") public class SseApplication extends ResourceConfig { public SseApplication() { register(SseFeature.class); property(ServerProperties.ASYNC_SERVICE_ENABLED, true); } } - Servlet容器(如Tomcat)的
web.xml中要开启Servlet异步支持:<servlet> <servlet-name>JerseySseServlet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>com.yourpackage.SseApplication</param-value> </init-param> <async-supported>true</async-supported> </servlet>
- Maven依赖要确保包含容器核心包:
调整资源方法的执行顺序
你当前是先执行订阅再发送初始消息,建议调换顺序:先发送初始消息确认上下文可用,再执行订阅逻辑。这样能更早发现上下文缺失的问题:@GET @Path("{id}") @Produces(MediaType.SERVER_SENT_EVENTS) public void orderUpdates(@PathParam("id") String id, @Context SseEventSink sink, @Context Sse sse, @Context Request request) { // 先打印请求上下文,确认是否有效 System.out.println("当前请求上下文:" + request); // 先发送初始消息 OutboundSseEvent initialMessage = sse.newEventBuilder().comment("Connection initiated").build(); sink.send(initialMessage); // 再执行订阅 service.subscribe(id, sink, sse); }如果打印的
request为空,说明资源类没有被Jersey正确扫描,或者自定义拦截器/过滤器破坏了上下文传递。避免阻塞原请求线程
如果service.subscribe是阻塞方法,会占用原请求线程导致SSE连接无法完成初始化。要确保订阅逻辑是异步非阻塞的,比如用Jersey的AsyncResponse或者自定义线程池处理后续事件推送。
内容的提问来源于stack exchange,提问作者Xipo

