我有一个异步 JAX-RS API,用于在 Jersey容器Servlet 2.22 中放置并在 Tomcat 7 上托管的长轮询客户端。
它看起来类似于下面显示的代码段。它在生产中运作良好。
平均有150个长轮询请求同时执行。它导致几乎相同数量的实时Tomcat HTTP连接(根据JMX指标)。对于这种低流量情况,使用普通的 HTTP-BIO 连接器没有问题。如果仅使用托管线程,则无法检测到运行时连接泄漏:)
@Path("/v1/events")
public class LiveEventsResource {
private static final long longPollTimeoutMs = 30000;
private ExternalResource externalResource = ExternalResourceProvider.provide();
private AsyncResponse asyncResponse;
@POST
@Path("/liveEvents")
@ManagedAsync
public void getResult(@Suspended final AsyncResponse asyncResponse, RequestPayload payload) {
this.asyncResponse = asyncResponse;
asyncResponse.setTimeout(longPollTimeoutMs, TimeUnit.MILLISECONDS);
asyncResponse.setTimeoutHandler(new TimeoutHandler() {
@Override
public void handleTimeout(AsyncResponse asyncResponseArg) {
try {
asyncResponseArg.cancel();
} finally {
cleanupResources();
}
}
});
startListeningForExternalEventsAndReturn(payload);
}
private void startListeningForExternalEventsAndReturn(RequestPayload payload) {
externalResource.register(new Listener() {
@Override
public void onEvent(Event event) {
respond(event);
}
});
}
private void respond(Event event) {
try {
asyncResponse.resume(event);
} catch (RuntimeException exception) {
asyncResponse.resume(exception);
} finally {
cleanupResources();
}
}
protected void cleanupResources() {
externalResource.cleanup();
}
}
我面临的问题是,在成功的 Tomcat 重新部署过程之后,实时连接的数量显然将增加到大约300,然后增加到450,并且在进行一些进一步的重新部署后,它将达到{{1为容器配置的限制。
API的客户端通过等待客户端超时(当然大于servlet端设置的超时)来处理重新部署,并再次开始轮询API。但他们保证只能同时发送一个请求。
连接计数监控图的形状给出了提示。取消部署后连接计数保持不变(即使maxConnection
也没有将连接释放回池中),并且当客户端再次开始长轮询时开始增加(分配新连接)。 事实上,在JVM终止之前,在上一个上下文中启动的正在进行的(暂停的)异步请求永远不会被释放!
经过一些挖掘后,通过分析未经释放的少量重新部署之后进行的堆转储不难发现,暂停的TimeOutHandler
(AsyncResponse
)实例仍保留在以前的Web应用程序上下文的内存中(可轻松过滤)按AsyncResponder
个实例分组的JQL查询。同样数量的过时Classloader
个实例在以前的上下文中存在于内存中也是非常可疑的。
我开始环顾 Jersey容器的取消部署相关的源代码,希望在org.apache.coyote.Request
时执行一些清理操作,为异步请求实现一些优雅的关闭过程。在@PreDestroy
的{{1}}或close()
方法中。
我乐观地猜测,在取消部署之前运行每个预定的dispose()
就可以解决问题。但是将默认的Provider
提供程序(TimeOutHandler
)替换为自定义实现并收集@BackgroundScheduler
的所有排队DefaultBackgroundSchedulerProvider
并最终调用TimeoutHandler
或{{1他们没有帮助。此阶段对于此清理可能为时已晚,因为请求范围已经关闭。
关于我的异步设置缺失的内容或者 Jersey 如何配置以释放 Servlet容器在重新部署时仍然挂起的连接的任何想法?< / p>