我正在运行一个简单的JerseyGrizzly HTTP服务器,它有一个资源来处理。服务器发送的事件.
该资源需要手动处理一个 Timer
实例来向所有连接的SSE客户端发送一些keep-alive数据,所以我使用了 @PostConstruct
和 @PreDestroy
注解来构造和取消该定时器(和它的后台线程)。
该应用程序是一个简单的控制台应用程序,并且没有嵌入任何容器环境,如Tomcat。启动web服务器后,它等待一个简单的 System.in.read()
并通过调用 shutdownNow() 在Grizzly HTTP服务器实例上。
当连接到SSE资源时,资源类被实例化,定时器被启动。断开与服务器的连接后,经过短暂的延迟,资源又被销毁,一切正常。
但是由于SSE资源的特性,客户端可能会无休止地连接,从而也会在内部保持资源实例的存在。
如果我现在尝试通过调用以下方法来关闭服务器 shutdownNow()
时,服务器实例会正确关闭,但不会调用任何的 @PreDestroy
方法的活动资源。这将导致我的资源与 Timer
实例,并且由于计时器仍然处于活动状态,这反过来又使java进程继续运行,即使HTTP服务器已经被关闭。
有没有什么方法可以检索到所有当前活跃的资源实例,这样我就可以在关闭HTTP服务器之前手动调用它们各自的destroy方法? 或者这可能是Grizzly服务器的资源实例管理中的一个bug?
使用的版本是
这是代码的精简版。
public class ServerSentEventResource {
@Context
private Sse sse;
private volatile SseBroadcaster broadcaster;
private final Timer timer = new Timer();
@PostConstruct
public void init() {
broadcaster = sse.newBroadcaster();
final TimerTask task = new TimerTask() {
// ...
};
timer.scheduleAtFixedRate(task, 0, 1000);
}
@PreDestroy
public void destroy() {
timer.cancel();
broadcaster.close();
}
@GET
@Produces(SseFeature.SERVER_SENT_EVENTS)
public void get(@Context final SseEventSink eventSink) {
broadcaster.register(eventSink);
}
}
public static void main(final String[] args) {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(ServerSentEventResource.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create("0.0.0.0:8888"), resourceConfig);
System.in.read();
server.shutdownNow();
// At this point there is still the resource instance around, keeping the timer running, thus keeping the JVM running :(
}
我也遇到了同样的问题。我能够通过在实际关闭服务器之前删除默认的网络监听器来解决这个问题......
httpServer.removeListener("grizzly");