Issue #12256 - Configuring Virtual Thread executor in Jetty Http client makes my Spring Boot app freeze
* Defaulted the number of selectors to 1 in HttpClientTransportOverHTTP, to align with ClientConnector. * Improved virtual thread documentation. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
ceee65a7e5
commit
8c943c4bfb
|
@ -15,6 +15,9 @@ package org.eclipse.jetty.docs.programming;
|
|||
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.VirtualThreadPool;
|
||||
|
@ -26,9 +29,26 @@ public class ArchitectureDocs
|
|||
{
|
||||
// tag::queuedVirtual[]
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
|
||||
// Simple, unlimited, virtual thread Executor.
|
||||
threadPool.setVirtualThreadsExecutor(Executors.newVirtualThreadPerTaskExecutor());
|
||||
|
||||
// Configurable, bounded, virtual thread executor (preferred).
|
||||
VirtualThreadPool virtualExecutor = new VirtualThreadPool();
|
||||
virtualExecutor.setMaxThreads(128);
|
||||
threadPool.setVirtualThreadsExecutor(virtualExecutor);
|
||||
|
||||
// For server-side usage.
|
||||
Server server = new Server(threadPool);
|
||||
|
||||
// Simple client-side usage.
|
||||
HttpClient client = new HttpClient();
|
||||
client.setExecutor(threadPool);
|
||||
|
||||
// Client-side usage with explicit HttpClientTransport.
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setExecutor(threadPool);
|
||||
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
|
||||
// end::queuedVirtual[]
|
||||
}
|
||||
|
||||
|
@ -38,8 +58,21 @@ public class ArchitectureDocs
|
|||
VirtualThreadPool threadPool = new VirtualThreadPool();
|
||||
// Limit the max number of current virtual threads.
|
||||
threadPool.setMaxThreads(200);
|
||||
// Track, with details, virtual threads usage.
|
||||
threadPool.setTracking(true);
|
||||
threadPool.setDetailedDump(true);
|
||||
|
||||
// For server-side usage.
|
||||
Server server = new Server(threadPool);
|
||||
|
||||
// Simple client-side usage.
|
||||
HttpClient client = new HttpClient();
|
||||
client.setExecutor(threadPool);
|
||||
|
||||
// Client-side usage with explicit HttpClientTransport.
|
||||
ClientConnector clientConnector = new ClientConnector();
|
||||
clientConnector.setExecutor(threadPool);
|
||||
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
|
||||
// end::virtualVirtual[]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -714,7 +714,6 @@ See also the xref:server/index.adoc#threadpool[section about configuring the thr
|
|||
The `threadpool-all-virtual` module allows you to configure the server-wide thread pool, similarly to what you can do with the <<threadpool,`threadpool`>> Jetty module, so that all threads are virtual threads, introduced as an official feature since Java 21.
|
||||
|
||||
CAUTION: Only use this module if you are using Java 21 or later.
|
||||
If you are using Java 19 or Java 20, use the <<threadpool-virtual-preview,`threadpool-virtual-preview`>> Jetty module instead.
|
||||
|
||||
The module properties to configure the thread pool are:
|
||||
|
||||
|
@ -724,17 +723,7 @@ include::{jetty-home}/modules/threadpool-all-virtual.mod[tags=documentation]
|
|||
|
||||
The property `jetty.threadpool.maxThreads` limits, using a `Semaphore`, the number of current virtual threads in use.
|
||||
|
||||
Limiting the number of current virtual threads helps to limit resource usage in applications, especially in case of load spikes.
|
||||
When an unlimited number of virtual threads is allowed, the server might be brought down due to resource (typically memory) exhaustion.
|
||||
|
||||
[CAUTION]
|
||||
====
|
||||
Even when using virtual threads, Jetty uses non-blocking I/O, and dedicates a thread to each `java.nio.channels.Selector` to perform the `Selector.select()` operation.
|
||||
|
||||
Currently (up to Java 22), calling `Selector.select()` from a virtual thread pins the carrier thread.
|
||||
|
||||
When using the `threadpool-all-virtual` Jetty module, if you have `N` selectors, then `N` carrier threads will be pinned by the virtual threads calling `Selector.select()`, possibly making your system less efficient, and at worst locking up the entire system if there are no carrier threads available to run virtual threads.
|
||||
====
|
||||
Please refer to the xref:programming-guide:arch/threads.adoc#thread-pool-virtual-threads[virtual threads section] of the Jetty Threading Architecture for more information about virtual threads and their pitfalls.
|
||||
|
||||
[[threadpool-virtual]]
|
||||
== Module `threadpool-virtual`
|
||||
|
|
|
@ -269,6 +269,35 @@ Defaulting the number of reserved threads to zero ensures that the <<execution-s
|
|||
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/ArchitectureDocs.java[tags=virtualVirtual]
|
||||
----
|
||||
|
||||
Despite the name, `VirtualThreadPool` does not pool virtual threads, but allows you to impose a limit on the maximum number of current virtual threads, in order to limit resource consumption.
|
||||
Despite the name, `VirtualThreadPool` does not pool virtual threads, but allows you to impose a limit on the maximum number of current virtual threads, using a `Semaphore`.
|
||||
|
||||
Furthermore, you can configure it to track virtual threads so that a xref:troubleshooting/component-dump.adoc[Jetty component dump] will show all virtual threads, including those that are unmounted.
|
||||
Limiting the number of current virtual threads helps to limit resource usage in applications, especially in case of load spikes.
|
||||
When an unlimited number of virtual threads is allowed, the server might be brought down due to resource (typically memory) exhaustion.
|
||||
|
||||
Furthermore, you can configure it to track virtual threads so that a xref:troubleshooting/component-dump.adoc[Jetty component dump] will show all virtual threads currently in use, including those that are unmounted.
|
||||
|
||||
[[thread-pool-virtual-threads-pinning]]
|
||||
==== Virtual Threads Pinning
|
||||
|
||||
Even when using virtual threads, Jetty uses non-blocking I/O, and dedicates a thread to each `java.nio.channels.Selector` to perform the `Selector.select()` operation.
|
||||
|
||||
Currently (up to Java 22), calling `Selector.select()` from a virtual thread *pins* the carrier thread.
|
||||
|
||||
If you configure a server-side `Connector`, or Jetty's `HttpClient` with `N` selectors, then `N` carrier threads will be pinned by the virtual threads calling `Selector.select()`.
|
||||
|
||||
If you have less than `N` CPU cores in your system, then by default all carriers will be pinned in the `Selector.select()` call, leaving no carrier to execute virtual threads, and therefore completely locking up your system, which will become completely unresponsive.
|
||||
|
||||
If you have more than `N` CPU cores in your system, then by default your system may be less efficient, since the carrier threads may be pinned in the `Selector.select()` call, and therefore not available to run virtual threads.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
The number of CPU cores of your system determines, by default, the number of carrier threads.
|
||||
The number of carrier threads may be explicitly configured by setting the system property `-Djdk.virtualThreadScheduler.parallelism=N`, where `N` is your desired number of carrier threads.
|
||||
|
||||
Selector threads used by Jetty pin carrier threads.
|
||||
|
||||
Choose the number of selectors wisely when using virtual threads: the number of selectors must always be less than the number of carrier threads, to leave some of the carrier threads free to run virtual threads.
|
||||
|
||||
As an extreme example, if your system only has `1` CPU core, then `1` selector is enough to pin the only carrier thread, and your system will eventually lock up.
|
||||
In this case, you must explicitly configure the number of carrier threads by setting the system property `-Djdk.virtualThreadScheduler.parallelism=2` (or to a larger value).
|
||||
====
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.eclipse.jetty.client.Origin;
|
|||
import org.eclipse.jetty.client.Request;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.ProcessorUtils;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -42,7 +41,7 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
|
|||
|
||||
public HttpClientTransportOverHTTP()
|
||||
{
|
||||
this(Math.max(1, ProcessorUtils.availableProcessors() / 2));
|
||||
this(1);
|
||||
}
|
||||
|
||||
public HttpClientTransportOverHTTP(int selectors)
|
||||
|
|
Loading…
Reference in New Issue