From b61c15c0dbd478cd876a7f86f79e576f00575b46 Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Tue, 16 Apr 2024 11:43:08 +0200 Subject: [PATCH] Jetty HTTP Container native timeout Signed-off-by: Maxim Nesen --- .../jersey/jetty/JettyHttpContainer.java | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java index 6a3805f805..c555e923d6 100644 --- a/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java +++ b/containers/jetty-http/src/main/java17/org/glassfish/jersey/jetty/JettyHttpContainer.java @@ -46,6 +46,7 @@ import org.eclipse.jetty.security.AuthenticationState; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.thread.Scheduler; import org.glassfish.jersey.internal.MapPropertiesDelegate; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.ReferencingFactory; @@ -91,8 +92,6 @@ public final class JettyHttpContainer extends Handler.Abstract implements Contai */ private boolean configSetStatusOverSendError; - private final ScheduledThreadPoolExecutor timeoutScheduler; - /** * Referencing factory for Jetty request. */ @@ -141,7 +140,7 @@ protected void configure() { @Override public boolean handle(Request request, Response response, Callback callback) throws Exception { - final ResponseWriter responseWriter = new ResponseWriter(timeoutScheduler, request, response, callback, configSetStatusOverSendError); + final ResponseWriter responseWriter = new ResponseWriter(request, response, callback, configSetStatusOverSendError); try { LOGGER.debugLog(LocalizationMessages.CONTAINER_STARTED()); final URI baseUri = getBaseUri(request); @@ -253,37 +252,38 @@ private String getBasePath(final Request request) { } } - private static final class ResponseWriter implements ContainerResponseWriter { + private static class ResponseWriter implements ContainerResponseWriter { private final Request request; private final Response response; private final Callback callback; private final boolean configSetStatusOverSendError; private final long asyncStartTimeNanos; - private final ScheduledExecutorService timeoutScheduler; + private final Scheduler scheduler; private final ConcurrentLinkedQueue timeoutHandlerQueue = new ConcurrentLinkedQueue<>(); - private ScheduledFuture currentTimerTask; + private Scheduler.Task currentTimerTask; - ResponseWriter(final ScheduledExecutorService timeoutScheduler, final Request request, final Response response, + ResponseWriter(final Request request, final Response response, final Callback callback, final boolean configSetStatusOverSendError) { - this.timeoutScheduler = timeoutScheduler; this.request = request; this.response = response; this.callback = callback; this.asyncStartTimeNanos = System.nanoTime(); this.configSetStatusOverSendError = configSetStatusOverSendError; + + this.scheduler = request.getComponents().getScheduler(); } private synchronized void setNewTimeout(long timeOut, TimeUnit timeUnit) { long timeOutNanos = timeUnit.toNanos(timeOut); if (currentTimerTask != null) { // Do not interrupt, see callTimeoutHandlers() - currentTimerTask.cancel(false); + currentTimerTask.cancel(); } // Use System.nanoTime() as the clock source here, because the returned value is not prone to wall-clock // drift - unlike System.currentTimeMillis(). long delayNanos = Math.max(asyncStartTimeNanos - System.nanoTime() + timeOutNanos, 0L); - currentTimerTask = timeoutScheduler.schedule(this::callTimeoutHandlers, delayNanos, TimeUnit.NANOSECONDS); + currentTimerTask = scheduler.schedule(this::callTimeoutHandlers, delayNanos, TimeUnit.NANOSECONDS); } private void callTimeoutHandlers() { @@ -437,20 +437,7 @@ public void doStop() throws Exception { appHandler.onShutdown(this); appHandler = null; - timeoutScheduler.shutdown(); boolean needInterrupt = false; - while (true) { - try { - if (timeoutScheduler.awaitTermination(1L, TimeUnit.MINUTES)) { - break; - } - } catch (InterruptedException e) { - if (!needInterrupt) { - needInterrupt = true; - timeoutScheduler.shutdownNow(); - } - } - } if (needInterrupt) { Thread.currentThread().interrupt(); } @@ -458,21 +445,6 @@ public void doStop() throws Exception { private static final AtomicInteger TIMEOUT_HANDLER_ID_GEN = new AtomicInteger(); - private static ScheduledThreadPoolExecutor createTimeoutScheduler() { - // Note: creating the thread-pool does not start the core-pool threads. - ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> { - Thread t = new Thread(r, "JettyHttpContainer-Timeout-Handler #" + TIMEOUT_HANDLER_ID_GEN.incrementAndGet()); - t.setDaemon(true); - return t; - }); - // Limit the number of timeout handling threads to a quarter of the number of CPUs, at least 2. - executor.setMaximumPoolSize(Math.max(2, Runtime.getRuntime().availableProcessors() / 4)); - executor.allowCoreThreadTimeOut(true); - // Don't Keep timeout handling threads around "forever". - executor.setKeepAliveTime(100, TimeUnit.MILLISECONDS); - return executor; - } - /** * Create a new Jetty HTTP container. * @@ -480,7 +452,6 @@ private static ScheduledThreadPoolExecutor createTimeoutScheduler() { * @param parentContext DI provider specific context with application's registered bindings. */ JettyHttpContainer(final Application application, final Object parentContext) { - this.timeoutScheduler = createTimeoutScheduler(); this.appHandler = new ApplicationHandler(application, new JettyBinder(), parentContext); } @@ -490,7 +461,6 @@ private static ScheduledThreadPoolExecutor createTimeoutScheduler() { * @param application JAX-RS / Jersey application to be deployed on Jetty HTTP container. */ JettyHttpContainer(final Application application) { - this.timeoutScheduler = createTimeoutScheduler(); this.appHandler = new ApplicationHandler(application, new JettyBinder()); cacheConfigSetStatusOverSendError(); @@ -502,7 +472,6 @@ private static ScheduledThreadPoolExecutor createTimeoutScheduler() { * @param applicationClass JAX-RS / Jersey class of application to be deployed on Jetty HTTP container. */ JettyHttpContainer(final Class applicationClass) { - this.timeoutScheduler = createTimeoutScheduler(); this.appHandler = new ApplicationHandler(applicationClass, new JettyBinder()); cacheConfigSetStatusOverSendError();