Skip to content

Commit

Permalink
Fixes #12154 - Limit concurrent virtual threads. (#12155)
Browse files Browse the repository at this point in the history
Updated VirtualThreadPool to limit the number of concurrent virtual threads using a Semaphore.

Updated modules and documentation.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
  • Loading branch information
sbordet committed Aug 23, 2024
1 parent ba4cc39 commit 9f08dc3
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,29 @@

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.VirtualThreadPool;

@SuppressWarnings("unused")
public class ArchitectureDocs
{
public void configureVirtualThreads()
public void queuedVirtualThreads()
{
// tag::virtual[]
// tag::queuedVirtual[]
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setVirtualThreadsExecutor(Executors.newVirtualThreadPerTaskExecutor());

Server server = new Server(threadPool);
// end::virtual[]
// end::queuedVirtual[]
}

public void virtualVirtualThreads()
{
// tag::virtualVirtual[]
VirtualThreadPool threadPool = new VirtualThreadPool();
// Limit the max number of current virtual threads.
threadPool.setMaxThreads(200);

Server server = new Server(threadPool);
// end::virtualVirtual[]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,34 @@ If you want to use virtual threads, introduced as a preview feature in Java 19 a

See also the xref:server/index.adoc#threadpool[section about configuring the thread pool].

[[threadpool-all-virtual]]
== Module `threadpool-all-virtual`

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:

----
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.
====

[[threadpool-virtual]]
== Module `threadpool-virtual`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,32 +328,30 @@ Virtual threads have been introduced as a preview feature in Java 19 and Java 20

The xref:modules/standard.adoc#threadpool-virtual-preview[`threadpool-virtual-preview`] Jetty module provides support for virtual threads in Java 19 and Java 20, and it is mutually exclusive with the `threadpool` Jetty module.

The xref:modules/standard.adoc#threadpool-virtual[`threadpool-virtual`] Jetty module provides support for virtual threads in Java 21 or later, and it is mutually exclusive with the `threadpool` Jetty module.
When using Java 21, there are two Jetty modules available:

* xref:modules/standard.adoc#threadpool-virtual[`threadpool-virtual`]
* xref:modules/standard.adoc#threadpool-all-virtual[`threadpool-all-virtual`]

Both are mutually exclusive with the `threadpool` Jetty module.

If you have already enabled the `threadpool` Jetty module, it is sufficient to remove it by removing the `$JETTY_BASE/start.d/threadpool.ini` file.

When using Java 21 or later, you can enable the xref:modules/standard.adoc#threadpool-virtual[`threadpool-virtual`] module:
The xref:modules/standard.adoc#threadpool-virtual[`threadpool-virtual`] Jetty module provides a mixed thread mode, where platform threads are used to run internal Jetty tasks, but application code is invoked using virtual threads.

The xref:modules/standard.adoc#threadpool-all-virtual[`threadpool-all-virtual`] Jetty module provides a thread mode where all threads are virtual threads, including those used internally by Jetty.

You can enable either module using:

----
$ java -jar $JETTY_HOME/start.jar --add-modules=threadpool-virtual,http
----

After the command above, the `$JETTY_BASE` directory looks like this:
or

[source]
----
$JETTY_BASE
├── resources
│ └── jetty-logging.properties
└── start.d
├── http.ini
└── threadpool-virtual.ini
$ java -jar $JETTY_HOME/start.jar --add-modules=threadpool-all-virtual,http
----

Now you can customize the `threadpool-virtual.ini` file to explicitly configure the thread pool and the virtual threads and then start Jetty:

[jetty%nowrap]
....
[jetty]
setupArgs=--add-modules=threadpool-virtual,http
....
After the command above, the `$JETTY_BASE/start.d/` directory will contain the corresponding `threadpool-virtual.ini` or `threadpool-all-virtual.ini` file.
You can now explicitly configure the thread pool module properties inside the `+*.ini+` file and then start Jetty.
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,14 @@ Virtual threads have been introduced in Java 19 and Java 20 as a preview feature

NOTE: In Java versions where virtual threads are a preview feature, remember to add `+--enable-preview+` to the JVM command line options to use virtual threads.

[[thread-pool-virtual-threads-queued]]
==== Virtual Threads Support with `QueuedThreadPool`

`QueuedThreadPool` can be configured to use virtual threads by specifying the virtual threads `Executor`:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/ArchitectureDocs.java[tags=virtual]
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/ArchitectureDocs.java[tags=queuedVirtual]
----

[CAUTION]
Expand All @@ -255,3 +258,17 @@ Enabling virtual threads in `QueuedThreadPool` will default the number of reserv
Defaulting the number of reserved threads to zero ensures that the <<execution-strategy-pec,Produce-Execute-Consume mode>> is always used, which means that virtual threads will always be used for blocking tasks.
====

[[thread-pool-virtual-threads-virtual]]
==== Virtual Threads Support with `VirtualThreadPool`

`VirtualThreadPool` is an alternative to `QueuedThreadPool` that creates only virtual threads (no platform threads).

[,java,indent=0]
----
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.

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.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

<Configure>
<New id="threadPool" class="org.eclipse.jetty.util.thread.VirtualThreadPool">
<Arg type="int"><Property name="jetty.threadPool.maxThreads" default="200" /></Arg>
<Set name="name" property="jetty.threadPool.namePrefix" />
<Set name="tracking" property="jetty.threadPool.tracking" />
<Set name="detailedDump" property="jetty.threadPool.detailedDump" />
</New>

<Call class="org.slf4j.LoggerFactory" name="getLogger">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,18 @@
<Set name="maxEvictCount" type="int"><Property name="jetty.threadPool.maxEvictCount" default="1"/></Set>
<Set name="detailedDump" type="boolean"><Property name="jetty.threadPool.detailedDump" default="false"/></Set>
<Get id="namePrefix" name="name" />
<Call class="java.lang.Thread" name="ofVirtual">
<Call class="java.lang.Thread$Builder" name="name">
<Arg>
<Property name="jetty.threadPool.virtual.namePrefix">
<Default><Ref refid="namePrefix" />-virtual-</Default>
</Property>
</Arg>
<Arg type="long">0</Arg>
<Call class="java.lang.Thread$Builder" name="inheritInheritableThreadLocals">
<Arg type="boolean"><Property name="jetty.threadPool.virtual.inheritInheritableThreadLocals" default="false" /></Arg>
<Call id="virtualThreadFactory" class="java.lang.Thread$Builder" name="factory" />
</Call>
</Call>
</Call>
<Call name="setVirtualThreadsExecutor">
<Arg>
<Call class="java.util.concurrent.Executors" name="newThreadPerTaskExecutor">
<Arg><Ref refid="virtualThreadFactory" /></Arg>
</Call>
<New class="org.eclipse.jetty.util.thread.VirtualThreadPool">
<Set name="name">
<Property name="jetty.threadPool.virtual.namePrefix">
<Default><Ref refid="namePrefix" />-virtual-</Default>
</Property>
</Set>
<Set name="maxThreads" property="jetty.threadPool.virtual.maxThreads" />
<Set name="tracking" property="jetty.threadPool.virtual.tracking" />
<Set name="detailedDump" property="jetty.threadPool.detailedDump" />
</New>
</Arg>
</Call>
</New>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ etc/jetty-threadpool-all-virtual.xml

[ini-template]
# tag::documentation[]
## Platform threads name prefix.
## Virtual threads name prefix.
#jetty.threadPool.namePrefix=vtp<hashCode>

## Maximum number of current virtual threads.
#jetty.threadPool.maxThreads=200

## Whether to track virtual threads so they appear
## in the dump even if they are unmounted.
#jetty.threadPool.tracking=false

## Whether to output virtual thread's stack traces in the dump.
#jetty.threadPool.detailedDump=false
# end::documentation[]
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ etc/jetty-threadpool-virtual.xml
## Virtual threads name prefix.
#jetty.threadPool.virtual.namePrefix=qtp<hashCode>-virtual-

## Whether virtual threads inherits the values of inheritable thread locals.
#jetty.threadPool.virtual.inheritInheritableThreadLocals=true
## Max number of current virtual threads.
#jetty.threadPool.virtual.maxThreads=200

## Whether to track virtual threads so they appear
## in the dump even if they are unmounted.
#jetty.threadPool.virtual.tracking=false
# end::documentation[]
Loading

0 comments on commit 9f08dc3

Please sign in to comment.